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
1272#[serde_as]
1274#[skip_serializing_none]
1275#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1276#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1277#[serde(rename_all = "camelCase")]
1278#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = FS_WRITE_TEXT_FILE_METHOD_NAME)))]
1279#[non_exhaustive]
1280pub struct WriteTextFileResponse {
1281 #[serde_as(deserialize_as = "DefaultOnError")]
1287 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1288 #[serde(default)]
1289 #[serde(rename = "_meta")]
1290 pub meta: Option<Meta>,
1291}
1292
1293impl WriteTextFileResponse {
1294 #[must_use]
1296 pub fn new() -> Self {
1297 Self::default()
1298 }
1299
1300 #[must_use]
1306 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1307 self.meta = meta.into_option();
1308 self
1309 }
1310}
1311
1312#[serde_as]
1318#[skip_serializing_none]
1319#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1320#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1321#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = FS_READ_TEXT_FILE_METHOD_NAME)))]
1322#[serde(rename_all = "camelCase")]
1323#[non_exhaustive]
1324pub struct ReadTextFileRequest {
1325 pub session_id: SessionId,
1327 pub path: PathBuf,
1329 #[serde_as(deserialize_as = "DefaultOnError")]
1331 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1332 #[serde(default)]
1333 pub line: Option<u32>,
1334 #[serde_as(deserialize_as = "DefaultOnError")]
1336 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1337 #[serde(default)]
1338 pub limit: Option<u32>,
1339 #[serde_as(deserialize_as = "DefaultOnError")]
1345 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1346 #[serde(default)]
1347 #[serde(rename = "_meta")]
1348 pub meta: Option<Meta>,
1349}
1350
1351impl ReadTextFileRequest {
1352 #[must_use]
1354 pub fn new(session_id: impl Into<SessionId>, path: impl Into<PathBuf>) -> Self {
1355 Self {
1356 session_id: session_id.into(),
1357 path: path.into(),
1358 line: None,
1359 limit: None,
1360 meta: None,
1361 }
1362 }
1363
1364 #[must_use]
1366 pub fn line(mut self, line: impl IntoOption<u32>) -> Self {
1367 self.line = line.into_option();
1368 self
1369 }
1370
1371 #[must_use]
1373 pub fn limit(mut self, limit: impl IntoOption<u32>) -> Self {
1374 self.limit = limit.into_option();
1375 self
1376 }
1377
1378 #[must_use]
1384 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1385 self.meta = meta.into_option();
1386 self
1387 }
1388}
1389
1390#[serde_as]
1392#[skip_serializing_none]
1393#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1394#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1395#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = FS_READ_TEXT_FILE_METHOD_NAME)))]
1396#[serde(rename_all = "camelCase")]
1397#[non_exhaustive]
1398pub struct ReadTextFileResponse {
1399 pub content: String,
1401 #[serde_as(deserialize_as = "DefaultOnError")]
1407 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1408 #[serde(default)]
1409 #[serde(rename = "_meta")]
1410 pub meta: Option<Meta>,
1411}
1412
1413impl ReadTextFileResponse {
1414 #[must_use]
1416 pub fn new(content: impl Into<String>) -> Self {
1417 Self {
1418 content: content.into(),
1419 meta: None,
1420 }
1421 }
1422
1423 #[must_use]
1429 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1430 self.meta = meta.into_option();
1431 self
1432 }
1433}
1434
1435#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1439#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
1440#[serde(transparent)]
1441#[from(Arc<str>, String, &'static str)]
1442#[non_exhaustive]
1443pub struct TerminalId(pub Arc<str>);
1444
1445impl TerminalId {
1446 #[must_use]
1448 pub fn new(id: impl Into<Arc<str>>) -> Self {
1449 Self(id.into())
1450 }
1451}
1452
1453#[serde_as]
1455#[skip_serializing_none]
1456#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1457#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1458#[serde(rename_all = "camelCase")]
1459#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_CREATE_METHOD_NAME)))]
1460#[non_exhaustive]
1461pub struct CreateTerminalRequest {
1462 pub session_id: SessionId,
1464 pub command: String,
1466 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1468 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1469 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1470 pub args: Vec<String>,
1471 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1473 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1474 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1475 pub env: Vec<EnvVariable>,
1476 #[serde_as(deserialize_as = "DefaultOnError")]
1478 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1479 #[serde(default)]
1480 pub cwd: Option<PathBuf>,
1481 #[serde_as(deserialize_as = "DefaultOnError")]
1490 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1491 #[serde(default)]
1492 pub output_byte_limit: Option<u64>,
1493 #[serde_as(deserialize_as = "DefaultOnError")]
1499 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1500 #[serde(default)]
1501 #[serde(rename = "_meta")]
1502 pub meta: Option<Meta>,
1503}
1504
1505impl CreateTerminalRequest {
1506 #[must_use]
1508 pub fn new(session_id: impl Into<SessionId>, command: impl Into<String>) -> Self {
1509 Self {
1510 session_id: session_id.into(),
1511 command: command.into(),
1512 args: Vec::new(),
1513 env: Vec::new(),
1514 cwd: None,
1515 output_byte_limit: None,
1516 meta: None,
1517 }
1518 }
1519
1520 #[must_use]
1522 pub fn args(mut self, args: Vec<String>) -> Self {
1523 self.args = args;
1524 self
1525 }
1526
1527 #[must_use]
1529 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
1530 self.env = env;
1531 self
1532 }
1533
1534 #[must_use]
1536 pub fn cwd(mut self, cwd: impl IntoOption<PathBuf>) -> Self {
1537 self.cwd = cwd.into_option();
1538 self
1539 }
1540
1541 #[must_use]
1550 pub fn output_byte_limit(mut self, output_byte_limit: impl IntoOption<u64>) -> Self {
1551 self.output_byte_limit = output_byte_limit.into_option();
1552 self
1553 }
1554
1555 #[must_use]
1561 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1562 self.meta = meta.into_option();
1563 self
1564 }
1565}
1566
1567#[serde_as]
1569#[skip_serializing_none]
1570#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1571#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1572#[serde(rename_all = "camelCase")]
1573#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_CREATE_METHOD_NAME)))]
1574#[non_exhaustive]
1575pub struct CreateTerminalResponse {
1576 pub terminal_id: TerminalId,
1578 #[serde_as(deserialize_as = "DefaultOnError")]
1584 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1585 #[serde(default)]
1586 #[serde(rename = "_meta")]
1587 pub meta: Option<Meta>,
1588}
1589
1590impl CreateTerminalResponse {
1591 #[must_use]
1593 pub fn new(terminal_id: impl Into<TerminalId>) -> Self {
1594 Self {
1595 terminal_id: terminal_id.into(),
1596 meta: None,
1597 }
1598 }
1599
1600 #[must_use]
1606 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1607 self.meta = meta.into_option();
1608 self
1609 }
1610}
1611
1612#[serde_as]
1614#[skip_serializing_none]
1615#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1616#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1617#[serde(rename_all = "camelCase")]
1618#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_OUTPUT_METHOD_NAME)))]
1619#[non_exhaustive]
1620pub struct TerminalOutputRequest {
1621 pub session_id: SessionId,
1623 pub terminal_id: TerminalId,
1625 #[serde_as(deserialize_as = "DefaultOnError")]
1631 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1632 #[serde(default)]
1633 #[serde(rename = "_meta")]
1634 pub meta: Option<Meta>,
1635}
1636
1637impl TerminalOutputRequest {
1638 #[must_use]
1640 pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1641 Self {
1642 session_id: session_id.into(),
1643 terminal_id: terminal_id.into(),
1644 meta: None,
1645 }
1646 }
1647
1648 #[must_use]
1654 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1655 self.meta = meta.into_option();
1656 self
1657 }
1658}
1659
1660#[serde_as]
1662#[skip_serializing_none]
1663#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1664#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1665#[serde(rename_all = "camelCase")]
1666#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_OUTPUT_METHOD_NAME)))]
1667#[non_exhaustive]
1668pub struct TerminalOutputResponse {
1669 pub output: String,
1671 pub truncated: bool,
1673 #[serde_as(deserialize_as = "DefaultOnError")]
1675 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1676 #[serde(default)]
1677 pub exit_status: Option<TerminalExitStatus>,
1678 #[serde_as(deserialize_as = "DefaultOnError")]
1684 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1685 #[serde(default)]
1686 #[serde(rename = "_meta")]
1687 pub meta: Option<Meta>,
1688}
1689
1690impl TerminalOutputResponse {
1691 #[must_use]
1693 pub fn new(output: impl Into<String>, truncated: bool) -> Self {
1694 Self {
1695 output: output.into(),
1696 truncated,
1697 exit_status: None,
1698 meta: None,
1699 }
1700 }
1701
1702 #[must_use]
1704 pub fn exit_status(mut self, exit_status: impl IntoOption<TerminalExitStatus>) -> Self {
1705 self.exit_status = exit_status.into_option();
1706 self
1707 }
1708
1709 #[must_use]
1715 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1716 self.meta = meta.into_option();
1717 self
1718 }
1719}
1720
1721#[serde_as]
1723#[skip_serializing_none]
1724#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1725#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1726#[serde(rename_all = "camelCase")]
1727#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_RELEASE_METHOD_NAME)))]
1728#[non_exhaustive]
1729pub struct ReleaseTerminalRequest {
1730 pub session_id: SessionId,
1732 pub terminal_id: TerminalId,
1734 #[serde_as(deserialize_as = "DefaultOnError")]
1740 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1741 #[serde(default)]
1742 #[serde(rename = "_meta")]
1743 pub meta: Option<Meta>,
1744}
1745
1746impl ReleaseTerminalRequest {
1747 #[must_use]
1749 pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1750 Self {
1751 session_id: session_id.into(),
1752 terminal_id: terminal_id.into(),
1753 meta: None,
1754 }
1755 }
1756
1757 #[must_use]
1763 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1764 self.meta = meta.into_option();
1765 self
1766 }
1767}
1768
1769#[serde_as]
1771#[skip_serializing_none]
1772#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1773#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1774#[serde(rename_all = "camelCase")]
1775#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_RELEASE_METHOD_NAME)))]
1776#[non_exhaustive]
1777pub struct ReleaseTerminalResponse {
1778 #[serde_as(deserialize_as = "DefaultOnError")]
1784 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1785 #[serde(default)]
1786 #[serde(rename = "_meta")]
1787 pub meta: Option<Meta>,
1788}
1789
1790impl ReleaseTerminalResponse {
1791 #[must_use]
1793 pub fn new() -> Self {
1794 Self::default()
1795 }
1796
1797 #[must_use]
1803 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1804 self.meta = meta.into_option();
1805 self
1806 }
1807}
1808
1809#[serde_as]
1811#[skip_serializing_none]
1812#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1813#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1814#[serde(rename_all = "camelCase")]
1815#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_KILL_METHOD_NAME)))]
1816#[non_exhaustive]
1817pub struct KillTerminalRequest {
1818 pub session_id: SessionId,
1820 pub terminal_id: TerminalId,
1822 #[serde_as(deserialize_as = "DefaultOnError")]
1828 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1829 #[serde(default)]
1830 #[serde(rename = "_meta")]
1831 pub meta: Option<Meta>,
1832}
1833
1834impl KillTerminalRequest {
1835 #[must_use]
1837 pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1838 Self {
1839 session_id: session_id.into(),
1840 terminal_id: terminal_id.into(),
1841 meta: None,
1842 }
1843 }
1844
1845 #[must_use]
1851 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1852 self.meta = meta.into_option();
1853 self
1854 }
1855}
1856
1857#[serde_as]
1859#[skip_serializing_none]
1860#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1861#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1862#[serde(rename_all = "camelCase")]
1863#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_KILL_METHOD_NAME)))]
1864#[non_exhaustive]
1865pub struct KillTerminalResponse {
1866 #[serde_as(deserialize_as = "DefaultOnError")]
1872 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1873 #[serde(default)]
1874 #[serde(rename = "_meta")]
1875 pub meta: Option<Meta>,
1876}
1877
1878impl KillTerminalResponse {
1879 #[must_use]
1881 pub fn new() -> Self {
1882 Self::default()
1883 }
1884
1885 #[must_use]
1891 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1892 self.meta = meta.into_option();
1893 self
1894 }
1895}
1896
1897#[serde_as]
1899#[skip_serializing_none]
1900#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1901#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1902#[serde(rename_all = "camelCase")]
1903#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_WAIT_FOR_EXIT_METHOD_NAME)))]
1904#[non_exhaustive]
1905pub struct WaitForTerminalExitRequest {
1906 pub session_id: SessionId,
1908 pub terminal_id: TerminalId,
1910 #[serde_as(deserialize_as = "DefaultOnError")]
1916 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1917 #[serde(default)]
1918 #[serde(rename = "_meta")]
1919 pub meta: Option<Meta>,
1920}
1921
1922impl WaitForTerminalExitRequest {
1923 #[must_use]
1925 pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1926 Self {
1927 session_id: session_id.into(),
1928 terminal_id: terminal_id.into(),
1929 meta: None,
1930 }
1931 }
1932
1933 #[must_use]
1939 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1940 self.meta = meta.into_option();
1941 self
1942 }
1943}
1944
1945#[serde_as]
1947#[skip_serializing_none]
1948#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1949#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1950#[serde(rename_all = "camelCase")]
1951#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_WAIT_FOR_EXIT_METHOD_NAME)))]
1952#[non_exhaustive]
1953pub struct WaitForTerminalExitResponse {
1954 #[serde(flatten)]
1956 pub exit_status: TerminalExitStatus,
1957 #[serde_as(deserialize_as = "DefaultOnError")]
1963 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1964 #[serde(default)]
1965 #[serde(rename = "_meta")]
1966 pub meta: Option<Meta>,
1967}
1968
1969impl WaitForTerminalExitResponse {
1970 #[must_use]
1972 pub fn new(exit_status: TerminalExitStatus) -> Self {
1973 Self {
1974 exit_status,
1975 meta: None,
1976 }
1977 }
1978
1979 #[must_use]
1985 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1986 self.meta = meta.into_option();
1987 self
1988 }
1989}
1990
1991#[serde_as]
1993#[skip_serializing_none]
1994#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1995#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1996#[serde(rename_all = "camelCase")]
1997#[non_exhaustive]
1998pub struct TerminalExitStatus {
1999 #[serde_as(deserialize_as = "DefaultOnError")]
2001 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2002 #[serde(default)]
2003 pub exit_code: Option<u32>,
2004 #[serde_as(deserialize_as = "DefaultOnError")]
2006 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2007 #[serde(default)]
2008 pub signal: Option<String>,
2009 #[serde_as(deserialize_as = "DefaultOnError")]
2015 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2016 #[serde(default)]
2017 #[serde(rename = "_meta")]
2018 pub meta: Option<Meta>,
2019}
2020
2021impl TerminalExitStatus {
2022 #[must_use]
2024 pub fn new() -> Self {
2025 Self::default()
2026 }
2027
2028 #[must_use]
2030 pub fn exit_code(mut self, exit_code: impl IntoOption<u32>) -> Self {
2031 self.exit_code = exit_code.into_option();
2032 self
2033 }
2034
2035 #[must_use]
2037 pub fn signal(mut self, signal: impl IntoOption<String>) -> Self {
2038 self.signal = signal.into_option();
2039 self
2040 }
2041
2042 #[must_use]
2048 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2049 self.meta = meta.into_option();
2050 self
2051 }
2052}
2053
2054#[serde_as]
2063#[skip_serializing_none]
2064#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2065#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2066#[serde(rename_all = "camelCase")]
2067#[non_exhaustive]
2068pub struct ClientCapabilities {
2069 #[serde_as(deserialize_as = "DefaultOnError")]
2072 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2073 #[serde(default)]
2074 pub fs: FileSystemCapabilities,
2075 #[serde_as(deserialize_as = "DefaultOnError")]
2077 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2078 #[serde(default)]
2079 pub terminal: bool,
2080 #[serde_as(deserialize_as = "DefaultOnError")]
2085 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2086 #[serde(default)]
2087 pub session: Option<ClientSessionCapabilities>,
2088 #[cfg(feature = "unstable_plan_operations")]
2097 #[serde_as(deserialize_as = "DefaultOnError")]
2098 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2099 #[serde(default)]
2100 pub plan: Option<PlanCapabilities>,
2101 #[serde_as(deserialize_as = "DefaultOnError")]
2105 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2106 #[serde(default)]
2107 pub auth: AuthCapabilities,
2108 #[serde_as(deserialize_as = "DefaultOnError")]
2114 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2115 #[serde(default)]
2116 pub elicitation: Option<ElicitationCapabilities>,
2117 #[cfg(feature = "unstable_nes")]
2126 #[serde_as(deserialize_as = "DefaultOnError")]
2127 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2128 #[serde(default)]
2129 pub nes: Option<ClientNesCapabilities>,
2130 #[cfg(feature = "unstable_nes")]
2136 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
2137 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2138 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2139 pub position_encodings: Vec<PositionEncodingKind>,
2140
2141 #[serde_as(deserialize_as = "DefaultOnError")]
2147 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2148 #[serde(default)]
2149 #[serde(rename = "_meta")]
2150 pub meta: Option<Meta>,
2151}
2152
2153impl ClientCapabilities {
2154 #[must_use]
2156 pub fn new() -> Self {
2157 Self::default()
2158 }
2159
2160 #[must_use]
2163 pub fn fs(mut self, fs: FileSystemCapabilities) -> Self {
2164 self.fs = fs;
2165 self
2166 }
2167
2168 #[must_use]
2170 pub fn terminal(mut self, terminal: bool) -> Self {
2171 self.terminal = terminal;
2172 self
2173 }
2174
2175 #[must_use]
2177 pub fn session(mut self, session: impl IntoOption<ClientSessionCapabilities>) -> Self {
2178 self.session = session.into_option();
2179 self
2180 }
2181
2182 #[cfg(feature = "unstable_plan_operations")]
2191 #[must_use]
2192 pub fn plan(mut self, plan: impl IntoOption<PlanCapabilities>) -> Self {
2193 self.plan = plan.into_option();
2194 self
2195 }
2196
2197 #[must_use]
2201 pub fn auth(mut self, auth: AuthCapabilities) -> Self {
2202 self.auth = auth;
2203 self
2204 }
2205
2206 #[must_use]
2209 pub fn elicitation(mut self, elicitation: impl IntoOption<ElicitationCapabilities>) -> Self {
2210 self.elicitation = elicitation.into_option();
2211 self
2212 }
2213
2214 #[cfg(feature = "unstable_nes")]
2218 #[must_use]
2219 pub fn nes(mut self, nes: impl IntoOption<ClientNesCapabilities>) -> Self {
2220 self.nes = nes.into_option();
2221 self
2222 }
2223
2224 #[cfg(feature = "unstable_nes")]
2228 #[must_use]
2229 pub fn position_encodings(mut self, position_encodings: Vec<PositionEncodingKind>) -> Self {
2230 self.position_encodings = position_encodings;
2231 self
2232 }
2233
2234 #[must_use]
2240 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2241 self.meta = meta.into_option();
2242 self
2243 }
2244}
2245
2246#[serde_as]
2248#[skip_serializing_none]
2249#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2250#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2251#[serde(rename_all = "camelCase")]
2252#[non_exhaustive]
2253pub struct ClientSessionCapabilities {
2254 #[cfg(feature = "unstable_session_compaction")]
2261 #[serde_as(deserialize_as = "DefaultOnError")]
2262 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2263 #[serde(default)]
2264 pub compaction: Option<CompactionCapabilities>,
2265 #[serde_as(deserialize_as = "DefaultOnError")]
2270 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2271 #[serde(default)]
2272 pub config_options: Option<SessionConfigOptionsCapabilities>,
2273 #[cfg(feature = "unstable_session_notices")]
2282 #[serde_as(deserialize_as = "DefaultOnError")]
2283 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2284 #[serde(default)]
2285 pub notices: Option<NoticeCapabilities>,
2286 #[serde_as(deserialize_as = "DefaultOnError")]
2292 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2293 #[serde(default)]
2294 #[serde(rename = "_meta")]
2295 pub meta: Option<Meta>,
2296}
2297
2298impl ClientSessionCapabilities {
2299 #[must_use]
2301 pub fn new() -> Self {
2302 Self::default()
2303 }
2304
2305 #[cfg(feature = "unstable_session_compaction")]
2307 #[must_use]
2308 pub fn compaction(mut self, compaction: impl IntoOption<CompactionCapabilities>) -> Self {
2309 self.compaction = compaction.into_option();
2310 self
2311 }
2312
2313 #[must_use]
2318 pub fn config_options(
2319 mut self,
2320 config_options: impl IntoOption<SessionConfigOptionsCapabilities>,
2321 ) -> Self {
2322 self.config_options = config_options.into_option();
2323 self
2324 }
2325
2326 #[cfg(feature = "unstable_session_notices")]
2328 #[must_use]
2329 pub fn notices(mut self, notices: impl IntoOption<NoticeCapabilities>) -> Self {
2330 self.notices = notices.into_option();
2331 self
2332 }
2333
2334 #[must_use]
2340 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2341 self.meta = meta.into_option();
2342 self
2343 }
2344}
2345
2346#[cfg(feature = "unstable_session_compaction")]
2352#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2353#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2354#[serde(rename_all = "camelCase")]
2355#[non_exhaustive]
2356pub struct CompactionCapabilities {}
2357
2358#[cfg(feature = "unstable_session_compaction")]
2359impl CompactionCapabilities {
2360 #[must_use]
2362 pub fn new() -> Self {
2363 Self {}
2364 }
2365}
2366
2367#[cfg(feature = "unstable_session_notices")]
2373#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2374#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2375#[serde(rename_all = "camelCase")]
2376#[non_exhaustive]
2377pub struct NoticeCapabilities {}
2378
2379#[cfg(feature = "unstable_session_notices")]
2380impl NoticeCapabilities {
2381 #[must_use]
2383 pub fn new() -> Self {
2384 Self {}
2385 }
2386}
2387
2388#[serde_as]
2390#[skip_serializing_none]
2391#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2392#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2393#[serde(rename_all = "camelCase")]
2394#[non_exhaustive]
2395pub struct SessionConfigOptionsCapabilities {
2396 #[serde_as(deserialize_as = "DefaultOnError")]
2403 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2404 #[serde(default)]
2405 pub boolean: Option<BooleanConfigOptionCapabilities>,
2406 #[serde_as(deserialize_as = "DefaultOnError")]
2412 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2413 #[serde(default)]
2414 #[serde(rename = "_meta")]
2415 pub meta: Option<Meta>,
2416}
2417
2418impl SessionConfigOptionsCapabilities {
2419 #[must_use]
2421 pub fn new() -> Self {
2422 Self::default()
2423 }
2424
2425 #[must_use]
2432 pub fn boolean(mut self, boolean: impl IntoOption<BooleanConfigOptionCapabilities>) -> Self {
2433 self.boolean = boolean.into_option();
2434 self
2435 }
2436
2437 #[must_use]
2443 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2444 self.meta = meta.into_option();
2445 self
2446 }
2447}
2448
2449#[serde_as]
2453#[skip_serializing_none]
2454#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2455#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2456#[non_exhaustive]
2457pub struct BooleanConfigOptionCapabilities {
2458 #[serde_as(deserialize_as = "DefaultOnError")]
2464 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2465 #[serde(default)]
2466 #[serde(rename = "_meta")]
2467 pub meta: Option<Meta>,
2468}
2469
2470impl BooleanConfigOptionCapabilities {
2471 #[must_use]
2473 pub fn new() -> Self {
2474 Self::default()
2475 }
2476
2477 #[must_use]
2483 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2484 self.meta = meta.into_option();
2485 self
2486 }
2487}
2488
2489#[serde_as]
2495#[skip_serializing_none]
2496#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2497#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2498#[serde(rename_all = "camelCase")]
2499#[non_exhaustive]
2500pub struct AuthCapabilities {
2501 #[serde_as(deserialize_as = "DefaultOnError")]
2507 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2508 #[serde(default)]
2509 pub terminal: bool,
2510 #[serde_as(deserialize_as = "DefaultOnError")]
2516 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2517 #[serde(default)]
2518 #[serde(rename = "_meta")]
2519 pub meta: Option<Meta>,
2520}
2521
2522impl AuthCapabilities {
2523 #[must_use]
2525 pub fn new() -> Self {
2526 Self::default()
2527 }
2528
2529 #[must_use]
2536 pub fn terminal(mut self, terminal: bool) -> Self {
2537 self.terminal = terminal;
2538 self
2539 }
2540
2541 #[must_use]
2547 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2548 self.meta = meta.into_option();
2549 self
2550 }
2551}
2552
2553#[serde_as]
2557#[skip_serializing_none]
2558#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2559#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2560#[serde(rename_all = "camelCase")]
2561#[non_exhaustive]
2562pub struct FileSystemCapabilities {
2563 #[serde_as(deserialize_as = "DefaultOnError")]
2565 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2566 #[serde(default)]
2567 pub read_text_file: bool,
2568 #[serde_as(deserialize_as = "DefaultOnError")]
2570 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2571 #[serde(default)]
2572 pub write_text_file: bool,
2573 #[serde_as(deserialize_as = "DefaultOnError")]
2579 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2580 #[serde(default)]
2581 #[serde(rename = "_meta")]
2582 pub meta: Option<Meta>,
2583}
2584
2585impl FileSystemCapabilities {
2586 #[must_use]
2588 pub fn new() -> Self {
2589 Self::default()
2590 }
2591
2592 #[must_use]
2594 pub fn read_text_file(mut self, read_text_file: bool) -> Self {
2595 self.read_text_file = read_text_file;
2596 self
2597 }
2598
2599 #[must_use]
2601 pub fn write_text_file(mut self, write_text_file: bool) -> Self {
2602 self.write_text_file = write_text_file;
2603 self
2604 }
2605
2606 #[must_use]
2612 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2613 self.meta = meta.into_option();
2614 self
2615 }
2616}
2617
2618#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2624#[non_exhaustive]
2625pub struct ClientMethodNames {
2626 pub session_request_permission: &'static str,
2628 pub session_update: &'static str,
2630 pub fs_write_text_file: &'static str,
2632 pub fs_read_text_file: &'static str,
2634 pub terminal_create: &'static str,
2636 pub terminal_output: &'static str,
2638 pub terminal_release: &'static str,
2640 pub terminal_wait_for_exit: &'static str,
2642 pub terminal_kill: &'static str,
2644 #[cfg(feature = "unstable_mcp_over_acp")]
2646 pub mcp_connect: &'static str,
2647 #[cfg(feature = "unstable_mcp_over_acp")]
2649 pub mcp_message: &'static str,
2650 #[cfg(feature = "unstable_mcp_over_acp")]
2652 pub mcp_disconnect: &'static str,
2653 pub elicitation_create: &'static str,
2655 pub elicitation_complete: &'static str,
2657}
2658
2659pub const CLIENT_METHOD_NAMES: ClientMethodNames = ClientMethodNames {
2661 session_update: SESSION_UPDATE_NOTIFICATION,
2662 session_request_permission: SESSION_REQUEST_PERMISSION_METHOD_NAME,
2663 fs_write_text_file: FS_WRITE_TEXT_FILE_METHOD_NAME,
2664 fs_read_text_file: FS_READ_TEXT_FILE_METHOD_NAME,
2665 terminal_create: TERMINAL_CREATE_METHOD_NAME,
2666 terminal_output: TERMINAL_OUTPUT_METHOD_NAME,
2667 terminal_release: TERMINAL_RELEASE_METHOD_NAME,
2668 terminal_wait_for_exit: TERMINAL_WAIT_FOR_EXIT_METHOD_NAME,
2669 terminal_kill: TERMINAL_KILL_METHOD_NAME,
2670 #[cfg(feature = "unstable_mcp_over_acp")]
2671 mcp_connect: MCP_CONNECT_METHOD_NAME,
2672 #[cfg(feature = "unstable_mcp_over_acp")]
2673 mcp_message: MCP_MESSAGE_METHOD_NAME,
2674 #[cfg(feature = "unstable_mcp_over_acp")]
2675 mcp_disconnect: MCP_DISCONNECT_METHOD_NAME,
2676 elicitation_create: ELICITATION_CREATE_METHOD_NAME,
2677 elicitation_complete: ELICITATION_COMPLETE_NOTIFICATION,
2678};
2679
2680pub(crate) const SESSION_UPDATE_NOTIFICATION: &str = "session/update";
2682pub(crate) const SESSION_REQUEST_PERMISSION_METHOD_NAME: &str = "session/request_permission";
2684pub(crate) const FS_WRITE_TEXT_FILE_METHOD_NAME: &str = "fs/write_text_file";
2686pub(crate) const FS_READ_TEXT_FILE_METHOD_NAME: &str = "fs/read_text_file";
2688pub(crate) const TERMINAL_CREATE_METHOD_NAME: &str = "terminal/create";
2690pub(crate) const TERMINAL_OUTPUT_METHOD_NAME: &str = "terminal/output";
2692pub(crate) const TERMINAL_RELEASE_METHOD_NAME: &str = "terminal/release";
2694pub(crate) const TERMINAL_WAIT_FOR_EXIT_METHOD_NAME: &str = "terminal/wait_for_exit";
2696pub(crate) const TERMINAL_KILL_METHOD_NAME: &str = "terminal/kill";
2698pub(crate) const ELICITATION_CREATE_METHOD_NAME: &str = "elicitation/create";
2700pub(crate) const ELICITATION_COMPLETE_NOTIFICATION: &str = "elicitation/complete";
2702
2703#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2710#[derive(Clone, Debug, Serialize, Deserialize)]
2711#[serde(untagged)]
2712#[cfg_attr(feature = "schemars", schemars(inline))]
2713#[non_exhaustive]
2714#[allow(clippy::large_enum_variant)]
2715pub enum AgentRequest {
2716 WriteTextFileRequest(WriteTextFileRequest),
2723 ReadTextFileRequest(ReadTextFileRequest),
2730 RequestPermissionRequest(RequestPermissionRequest),
2741 CreateTerminalRequest(CreateTerminalRequest),
2756 TerminalOutputRequest(TerminalOutputRequest),
2763 ReleaseTerminalRequest(ReleaseTerminalRequest),
2776 WaitForTerminalExitRequest(WaitForTerminalExitRequest),
2780 KillTerminalRequest(KillTerminalRequest),
2793 CreateElicitationRequest(CreateElicitationRequest),
2797 #[cfg(feature = "unstable_mcp_over_acp")]
2803 ConnectMcpRequest(ConnectMcpRequest),
2804 #[cfg(feature = "unstable_mcp_over_acp")]
2810 MessageMcpRequest(MessageMcpRequest),
2811 #[cfg(feature = "unstable_mcp_over_acp")]
2817 DisconnectMcpRequest(DisconnectMcpRequest),
2818 ExtMethodRequest(ExtRequest),
2826}
2827
2828impl AgentRequest {
2829 #[must_use]
2831 pub fn method(&self) -> &str {
2832 match self {
2833 Self::WriteTextFileRequest(_) => CLIENT_METHOD_NAMES.fs_write_text_file,
2834 Self::ReadTextFileRequest(_) => CLIENT_METHOD_NAMES.fs_read_text_file,
2835 Self::RequestPermissionRequest(_) => CLIENT_METHOD_NAMES.session_request_permission,
2836 Self::CreateTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_create,
2837 Self::TerminalOutputRequest(_) => CLIENT_METHOD_NAMES.terminal_output,
2838 Self::ReleaseTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_release,
2839 Self::WaitForTerminalExitRequest(_) => CLIENT_METHOD_NAMES.terminal_wait_for_exit,
2840 Self::KillTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_kill,
2841 Self::CreateElicitationRequest(_) => CLIENT_METHOD_NAMES.elicitation_create,
2842 #[cfg(feature = "unstable_mcp_over_acp")]
2843 Self::ConnectMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_connect,
2844 #[cfg(feature = "unstable_mcp_over_acp")]
2845 Self::MessageMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_message,
2846 #[cfg(feature = "unstable_mcp_over_acp")]
2847 Self::DisconnectMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_disconnect,
2848 Self::ExtMethodRequest(ext_request) => &ext_request.method,
2849 }
2850 }
2851}
2852
2853#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2860#[derive(Clone, Debug, Serialize, Deserialize)]
2861#[serde(untagged)]
2862#[cfg_attr(feature = "schemars", schemars(inline))]
2863#[non_exhaustive]
2864pub enum ClientResponse {
2865 WriteTextFileResponse(#[serde(default)] WriteTextFileResponse),
2867 ReadTextFileResponse(ReadTextFileResponse),
2869 RequestPermissionResponse(RequestPermissionResponse),
2871 CreateTerminalResponse(CreateTerminalResponse),
2873 TerminalOutputResponse(TerminalOutputResponse),
2875 ReleaseTerminalResponse(#[serde(default)] ReleaseTerminalResponse),
2877 WaitForTerminalExitResponse(WaitForTerminalExitResponse),
2879 KillTerminalResponse(#[serde(default)] KillTerminalResponse),
2881 CreateElicitationResponse(CreateElicitationResponse),
2883 #[cfg(feature = "unstable_mcp_over_acp")]
2885 ConnectMcpResponse(ConnectMcpResponse),
2886 #[cfg(feature = "unstable_mcp_over_acp")]
2888 DisconnectMcpResponse(#[serde(default)] DisconnectMcpResponse),
2889 #[cfg(feature = "unstable_mcp_over_acp")]
2891 MessageMcpResponse(MessageMcpResponse),
2892 ExtMethodResponse(ExtResponse),
2894}
2895
2896#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2903#[derive(Clone, Debug, Serialize, Deserialize)]
2904#[serde(untagged)]
2905#[expect(clippy::large_enum_variant)]
2906#[cfg_attr(feature = "schemars", schemars(inline))]
2907#[non_exhaustive]
2908pub enum AgentNotification {
2909 SessionNotification(SessionNotification),
2921 CompleteElicitationNotification(CompleteElicitationNotification),
2925 #[cfg(feature = "unstable_mcp_over_acp")]
2931 MessageMcpNotification(MessageMcpNotification),
2932 ExtNotification(ExtNotification),
2940}
2941
2942impl AgentNotification {
2943 #[must_use]
2945 pub fn method(&self) -> &str {
2946 match self {
2947 Self::SessionNotification(_) => CLIENT_METHOD_NAMES.session_update,
2948 Self::CompleteElicitationNotification(_) => CLIENT_METHOD_NAMES.elicitation_complete,
2949 #[cfg(feature = "unstable_mcp_over_acp")]
2950 Self::MessageMcpNotification(_) => CLIENT_METHOD_NAMES.mcp_message,
2951 Self::ExtNotification(ext_notification) => &ext_notification.method,
2952 }
2953 }
2954}
2955
2956#[cfg(test)]
2957mod tests {
2958 use super::*;
2959
2960 #[cfg(feature = "unstable_session_notices")]
2961 #[test]
2962 fn notice_preserves_wire_shape_nullable_fields_and_open_severity() {
2963 use serde_json::json;
2964
2965 let mut meta = Meta::new();
2966 meta.insert("source".into(), json!("fallback"));
2967 assert_eq!(
2968 serde_json::to_value(SessionUpdate::Notice(
2969 Notice::new(NoticeSeverity::Warning, "MCP server unavailable")
2970 .description("Continuing without it.")
2971 .meta(meta),
2972 ))
2973 .unwrap(),
2974 json!({
2975 "sessionUpdate": "notice",
2976 "severity": "warning",
2977 "title": "MCP server unavailable",
2978 "description": "Continuing without it.",
2979 "_meta": { "source": "fallback" }
2980 })
2981 );
2982
2983 assert_eq!(
2984 serde_json::to_value(SessionUpdate::Notice(Notice::new(
2985 NoticeSeverity::Info,
2986 "Indexing workspace",
2987 )))
2988 .unwrap(),
2989 json!({
2990 "sessionUpdate": "notice",
2991 "severity": "info",
2992 "title": "Indexing workspace"
2993 })
2994 );
2995
2996 let SessionUpdate::Notice(notice) = serde_json::from_value(json!({
2997 "sessionUpdate": "notice",
2998 "severity": "critical",
2999 "title": "Provider degraded",
3000 "description": null,
3001 "_meta": null
3002 }))
3003 .unwrap() else {
3004 panic!("expected notice");
3005 };
3006
3007 assert_eq!(
3008 notice.severity,
3009 NoticeSeverity::Other("critical".to_string())
3010 );
3011 assert_eq!(notice.description, None);
3012 assert_eq!(notice.meta, None);
3013 assert_eq!(
3014 serde_json::to_value(SessionUpdate::Notice(notice)).unwrap(),
3015 json!({
3016 "sessionUpdate": "notice",
3017 "severity": "critical",
3018 "title": "Provider degraded"
3019 })
3020 );
3021 }
3022
3023 #[cfg(feature = "unstable_session_notices")]
3024 #[test]
3025 fn notice_requires_non_null_severity_and_title() {
3026 use serde_json::json;
3027
3028 for malformed in [
3029 json!({
3030 "sessionUpdate": "notice",
3031 "severity": "warning"
3032 }),
3033 json!({
3034 "sessionUpdate": "notice",
3035 "severity": "warning",
3036 "title": null
3037 }),
3038 json!({
3039 "sessionUpdate": "notice",
3040 "title": "MCP server unavailable"
3041 }),
3042 json!({
3043 "sessionUpdate": "notice",
3044 "severity": null,
3045 "title": "MCP server unavailable"
3046 }),
3047 ] {
3048 assert!(serde_json::from_value::<SessionUpdate>(malformed).is_err());
3049 }
3050 }
3051
3052 #[cfg(feature = "unstable_session_notices")]
3053 #[test]
3054 fn notice_capability_advertises_support_only_when_present() {
3055 use serde_json::json;
3056
3057 let capabilities = ClientCapabilities::new()
3058 .session(ClientSessionCapabilities::new().notices(NoticeCapabilities::new()));
3059 let value = serde_json::to_value(&capabilities).unwrap();
3060 assert_eq!(value["session"], json!({ "notices": {} }));
3061 assert_eq!(
3062 serde_json::from_value::<ClientCapabilities>(value).unwrap(),
3063 capabilities
3064 );
3065
3066 for unsupported in [
3067 json!({}),
3068 json!({ "session": null }),
3069 json!({ "session": {} }),
3070 json!({ "session": { "notices": null } }),
3071 json!({ "session": { "notices": false } }),
3072 json!({ "session": { "notices": true } }),
3073 json!({ "session": { "notices": "supported" } }),
3074 ] {
3075 let capabilities: ClientCapabilities = serde_json::from_value(unsupported).unwrap();
3076 assert!(
3077 capabilities
3078 .session
3079 .and_then(|session| session.notices)
3080 .is_none()
3081 );
3082 }
3083
3084 assert_eq!(
3085 serde_json::to_value(
3086 ClientSessionCapabilities::new()
3087 .notices(NoticeCapabilities::new())
3088 .notices(None)
3089 )
3090 .unwrap(),
3091 json!({})
3092 );
3093 }
3094
3095 #[cfg(not(feature = "unstable_session_notices"))]
3096 #[test]
3097 fn unsupported_notice_capability_is_ignored() {
3098 use serde_json::json;
3099
3100 let capabilities: ClientSessionCapabilities =
3101 serde_json::from_value(json!({ "notices": {} })).unwrap();
3102 assert_eq!(serde_json::to_value(capabilities).unwrap(), json!({}));
3103 }
3104
3105 #[cfg(not(feature = "unstable_session_notices"))]
3106 #[test]
3107 fn unsupported_notice_is_rejected_by_closed_update_union() {
3108 use serde_json::json;
3109
3110 assert!(
3111 serde_json::from_value::<SessionUpdate>(json!({
3112 "sessionUpdate": "notice",
3113 "severity": "warning",
3114 "title": "MCP server unavailable"
3115 }))
3116 .is_err()
3117 );
3118 }
3119
3120 #[cfg(feature = "unstable_session_compaction")]
3121 #[test]
3122 fn compaction_updates_preserve_patch_and_open_status_semantics() {
3123 use serde_json::json;
3124
3125 assert_eq!(
3126 serde_json::to_value(SessionUpdate::CompactionUpdate(CompactionUpdate::new(
3127 "cmp_001",
3128 CompactionStatus::InProgress,
3129 )))
3130 .unwrap(),
3131 json!({
3132 "sessionUpdate": "compaction_update",
3133 "compactionId": "cmp_001",
3134 "status": "in_progress"
3135 })
3136 );
3137
3138 let SessionUpdate::CompactionUpdate(update) = serde_json::from_value(json!({
3139 "sessionUpdate": "compaction_update",
3140 "compactionId": "cmp_001",
3141 "status": "paused",
3142 "summary": null,
3143 "error": "waiting"
3144 }))
3145 .unwrap() else {
3146 panic!("expected compaction update");
3147 };
3148 assert_eq!(update.status, CompactionStatus::Other("paused".into()));
3149 assert!(update.summary.is_null());
3150 assert_eq!(update.error.value().map(String::as_str), Some("waiting"));
3151 assert!(update.meta.is_undefined());
3152 }
3153
3154 #[cfg(feature = "unstable_session_compaction")]
3155 #[test]
3156 fn compaction_chunk_and_v1_capability_serialize() {
3157 use serde_json::json;
3158
3159 assert_eq!(
3160 serde_json::to_value(SessionUpdate::CompactionSummaryChunk(
3161 CompactionSummaryChunk::new(
3162 "cmp_001",
3163 ContentBlock::Text(crate::v1::TextContent::new("retained")),
3164 ),
3165 ))
3166 .unwrap(),
3167 json!({
3168 "sessionUpdate": "compaction_summary_chunk",
3169 "compactionId": "cmp_001",
3170 "content": { "type": "text", "text": "retained" }
3171 })
3172 );
3173 assert_eq!(
3174 serde_json::to_value(
3175 ClientSessionCapabilities::new().compaction(CompactionCapabilities::new())
3176 )
3177 .unwrap(),
3178 json!({ "compaction": {} })
3179 );
3180 let absent: ClientSessionCapabilities = serde_json::from_value(json!({})).unwrap();
3181 let null: ClientSessionCapabilities =
3182 serde_json::from_value(json!({ "compaction": null })).unwrap();
3183 assert!(absent.compaction.is_none());
3184 assert!(null.compaction.is_none());
3185 }
3186
3187 #[test]
3188 fn test_elicitation_capability_semantics() {
3189 use serde_json::json;
3190
3191 let unsupported: ClientCapabilities = serde_json::from_value(json!({})).unwrap();
3192 assert!(unsupported.elicitation.is_none());
3193
3194 let null: ClientCapabilities =
3195 serde_json::from_value(json!({ "elicitation": null })).unwrap();
3196 assert!(null.elicitation.is_none());
3197
3198 let malformed: ClientCapabilities =
3199 serde_json::from_value(json!({ "elicitation": false })).unwrap();
3200 assert!(malformed.elicitation.is_none());
3201
3202 let empty: ClientCapabilities =
3203 serde_json::from_value(json!({ "elicitation": {} })).unwrap();
3204 let empty = empty.elicitation.expect("present capability");
3205 assert!(!empty.supports_form());
3206 assert!(!empty.supports_url());
3207
3208 let form_only: ClientCapabilities = serde_json::from_value(json!({
3209 "elicitation": { "form": {} }
3210 }))
3211 .unwrap();
3212 let form_only = form_only.elicitation.expect("advertised capability");
3213 assert!(form_only.supports_form());
3214 assert!(!form_only.supports_url());
3215
3216 let url_only: ClientCapabilities = serde_json::from_value(json!({
3217 "elicitation": { "url": {} }
3218 }))
3219 .unwrap();
3220 let url_only = url_only.elicitation.expect("advertised capability");
3221 assert!(!url_only.supports_form());
3222 assert!(url_only.supports_url());
3223
3224 let both: ClientCapabilities = serde_json::from_value(json!({
3225 "elicitation": { "form": {}, "url": {} }
3226 }))
3227 .unwrap();
3228 let both = both.elicitation.expect("advertised capability");
3229 assert!(both.supports_form());
3230 assert!(both.supports_url());
3231 }
3232
3233 #[test]
3234 fn test_elicitation_method_routing_and_envelopes() {
3235 use serde_json::json;
3236
3237 assert_eq!(CLIENT_METHOD_NAMES.elicitation_create, "elicitation/create");
3238 assert_eq!(
3239 CLIENT_METHOD_NAMES.elicitation_complete,
3240 "elicitation/complete"
3241 );
3242
3243 let request = AgentRequest::CreateElicitationRequest(CreateElicitationRequest::new(
3244 crate::v1::ElicitationFormMode::new(
3245 crate::v1::ElicitationSessionScope::new("sess_1"),
3246 crate::v1::ElicitationSchema::new(),
3247 ),
3248 "Choose a value",
3249 ));
3250 assert_eq!(request.method(), "elicitation/create");
3251 let method = Arc::from(request.method());
3252 let request = crate::v1::JsonRpcMessage::wrap(crate::v1::Request {
3253 id: crate::v1::RequestId::Number(7),
3254 method,
3255 params: Some(request),
3256 });
3257 assert_eq!(
3258 serde_json::to_value(request).unwrap(),
3259 json!({
3260 "jsonrpc": "2.0",
3261 "id": 7,
3262 "method": "elicitation/create",
3263 "params": {
3264 "mode": "form",
3265 "sessionId": "sess_1",
3266 "message": "Choose a value",
3267 "requestedSchema": { "type": "object", "properties": {} }
3268 }
3269 })
3270 );
3271
3272 let notification = AgentNotification::CompleteElicitationNotification(
3273 CompleteElicitationNotification::new("elic_1"),
3274 );
3275 assert_eq!(notification.method(), "elicitation/complete");
3276 let method = Arc::from(notification.method());
3277 let notification = crate::v1::JsonRpcMessage::wrap(crate::v1::Notification {
3278 method,
3279 params: Some(notification),
3280 });
3281 assert_eq!(
3282 serde_json::to_value(notification).unwrap(),
3283 json!({
3284 "jsonrpc": "2.0",
3285 "method": "elicitation/complete",
3286 "params": { "elicitationId": "elic_1" }
3287 })
3288 );
3289 }
3290
3291 #[test]
3292 fn test_client_capabilities_default_on_malformed_values() {
3293 use serde_json::json;
3294
3295 let capabilities: ClientCapabilities = serde_json::from_value(json!({
3296 "fs": {
3297 "readTextFile": "yes",
3298 "writeTextFile": true
3299 },
3300 "terminal": {}
3301 }))
3302 .unwrap();
3303
3304 assert!(!capabilities.fs.read_text_file);
3305 assert!(capabilities.fs.write_text_file);
3306 assert!(!capabilities.terminal);
3307
3308 let capabilities: ClientCapabilities = serde_json::from_value(json!({
3309 "fs": false
3310 }))
3311 .unwrap();
3312 assert_eq!(capabilities.fs, FileSystemCapabilities::default());
3313
3314 {
3315 let capabilities: ClientCapabilities = serde_json::from_value(json!({
3316 "auth": false
3317 }))
3318 .unwrap();
3319 assert_eq!(capabilities.auth, AuthCapabilities::default());
3320
3321 let capabilities: AuthCapabilities = serde_json::from_value(json!({
3322 "terminal": {}
3323 }))
3324 .unwrap();
3325 assert!(!capabilities.terminal);
3326 }
3327 }
3328
3329 #[test]
3330 fn test_serialization_behavior() {
3331 use serde_json::json;
3332
3333 assert_eq!(
3334 serde_json::from_value::<SessionInfoUpdate>(json!({})).unwrap(),
3335 SessionInfoUpdate {
3336 title: MaybeUndefined::Undefined,
3337 updated_at: MaybeUndefined::Undefined,
3338 meta: None
3339 }
3340 );
3341 assert_eq!(
3342 serde_json::from_value::<SessionInfoUpdate>(json!({"title": null, "updatedAt": null}))
3343 .unwrap(),
3344 SessionInfoUpdate {
3345 title: MaybeUndefined::Null,
3346 updated_at: MaybeUndefined::Null,
3347 meta: None
3348 }
3349 );
3350 assert_eq!(
3351 serde_json::from_value::<SessionInfoUpdate>(
3352 json!({"title": "title", "updatedAt": "timestamp"})
3353 )
3354 .unwrap(),
3355 SessionInfoUpdate {
3356 title: MaybeUndefined::Value("title".to_string()),
3357 updated_at: MaybeUndefined::Value("timestamp".to_string()),
3358 meta: None
3359 }
3360 );
3361
3362 assert_eq!(
3363 serde_json::to_value(SessionInfoUpdate::new()).unwrap(),
3364 json!({})
3365 );
3366 assert_eq!(
3367 serde_json::to_value(SessionInfoUpdate::new().title("title")).unwrap(),
3368 json!({"title": "title"})
3369 );
3370 assert_eq!(
3371 serde_json::to_value(SessionInfoUpdate::new().title(None)).unwrap(),
3372 json!({"title": null})
3373 );
3374 assert_eq!(
3375 serde_json::to_value(
3376 SessionInfoUpdate::new()
3377 .title("title")
3378 .title(MaybeUndefined::Undefined)
3379 )
3380 .unwrap(),
3381 json!({})
3382 );
3383 }
3384
3385 #[test]
3386 fn test_content_chunk_message_id_serialization() {
3387 use serde_json::json;
3388
3389 assert_eq!(
3390 serde_json::to_value(SessionUpdate::AgentMessageChunk(ContentChunk::new(
3391 ContentBlock::Text(crate::v1::TextContent::new("Hello"))
3392 )))
3393 .unwrap(),
3394 json!({
3395 "sessionUpdate": "agent_message_chunk",
3396 "content": {
3397 "type": "text",
3398 "text": "Hello"
3399 }
3400 })
3401 );
3402
3403 assert_eq!(
3404 serde_json::to_value(SessionUpdate::AgentMessageChunk(
3405 ContentChunk::new(ContentBlock::Text(crate::v1::TextContent::new("Hello")))
3406 .message_id("msg_agent_c42b9")
3407 ))
3408 .unwrap(),
3409 json!({
3410 "sessionUpdate": "agent_message_chunk",
3411 "messageId": "msg_agent_c42b9",
3412 "content": {
3413 "type": "text",
3414 "text": "Hello"
3415 }
3416 })
3417 );
3418
3419 let SessionUpdate::AgentMessageChunk(chunk) = serde_json::from_value(json!({
3420 "sessionUpdate": "agent_message_chunk",
3421 "messageId": null,
3422 "content": {
3423 "type": "text",
3424 "text": "Hello"
3425 }
3426 }))
3427 .unwrap() else {
3428 panic!("expected agent message chunk");
3429 };
3430
3431 assert_eq!(chunk.message_id, None);
3432 }
3433
3434 #[test]
3435 fn test_usage_update_serialization() {
3436 use serde_json::json;
3437
3438 assert_eq!(
3439 serde_json::to_value(SessionUpdate::UsageUpdate(UsageUpdate::new(
3440 53_000, 200_000
3441 )))
3442 .unwrap(),
3443 json!({
3444 "sessionUpdate": "usage_update",
3445 "used": 53000,
3446 "size": 200_000
3447 })
3448 );
3449
3450 assert_eq!(
3451 serde_json::to_value(SessionUpdate::UsageUpdate(
3452 UsageUpdate::new(53_000, 200_000).cost(Cost::new(0.045, "USD"))
3453 ))
3454 .unwrap(),
3455 json!({
3456 "sessionUpdate": "usage_update",
3457 "used": 53000,
3458 "size": 200_000,
3459 "cost": {
3460 "amount": 0.045,
3461 "currency": "USD"
3462 }
3463 })
3464 );
3465
3466 let SessionUpdate::UsageUpdate(update) = serde_json::from_value(json!({
3467 "sessionUpdate": "usage_update",
3468 "used": 53000,
3469 "size": 200_000,
3470 "cost": null
3471 }))
3472 .unwrap() else {
3473 panic!("expected usage update");
3474 };
3475
3476 assert_eq!(update.cost, None);
3477 }
3478
3479 #[cfg(feature = "unstable_nes")]
3480 #[test]
3481 fn test_client_capabilities_position_encodings_serialization() {
3482 use serde_json::json;
3483
3484 let capabilities = ClientCapabilities::new().position_encodings(vec![
3485 PositionEncodingKind::Utf32,
3486 PositionEncodingKind::Utf16,
3487 ]);
3488 let json = serde_json::to_value(&capabilities).unwrap();
3489
3490 assert_eq!(json["positionEncodings"], json!(["utf-32", "utf-16"]));
3491 }
3492
3493 #[test]
3494 fn test_client_capabilities_boolean_config_options_serialization() {
3495 use serde_json::json;
3496
3497 let capabilities = ClientCapabilities::new().session(
3498 ClientSessionCapabilities::new().config_options(
3499 SessionConfigOptionsCapabilities::new()
3500 .boolean(BooleanConfigOptionCapabilities::new()),
3501 ),
3502 );
3503 let json = serde_json::to_value(&capabilities).unwrap();
3504
3505 assert_eq!(json["session"]["configOptions"]["boolean"], json!({}));
3506
3507 let omitted: ClientCapabilities = serde_json::from_value(json!({})).unwrap();
3508 assert!(omitted.session.is_none());
3509
3510 let null_session: ClientCapabilities = serde_json::from_value(json!({
3511 "session": null
3512 }))
3513 .unwrap();
3514 assert!(null_session.session.is_none());
3515
3516 let null_config_options: ClientCapabilities = serde_json::from_value(json!({
3517 "session": {
3518 "configOptions": null
3519 }
3520 }))
3521 .unwrap();
3522 assert!(
3523 null_config_options
3524 .session
3525 .and_then(|session| session.config_options)
3526 .is_none()
3527 );
3528
3529 let null_boolean: ClientCapabilities = serde_json::from_value(json!({
3530 "session": {
3531 "configOptions": {
3532 "boolean": null
3533 }
3534 }
3535 }))
3536 .unwrap();
3537 assert!(
3538 null_boolean
3539 .session
3540 .and_then(|session| session.config_options)
3541 .and_then(|config_options| config_options.boolean)
3542 .is_none()
3543 );
3544 }
3545
3546 #[cfg(feature = "unstable_plan_operations")]
3547 #[test]
3548 fn test_plan_operations_serialization() {
3549 use serde_json::json;
3550
3551 use crate::v1::{PlanEntry, PlanEntryPriority, PlanEntryStatus, PlanUpdateContent};
3552
3553 let plan_update = SessionUpdate::PlanUpdate(PlanUpdate::new(PlanUpdateContent::items(
3554 "plan-1",
3555 vec![PlanEntry::new(
3556 "Step 1",
3557 PlanEntryPriority::High,
3558 PlanEntryStatus::Pending,
3559 )],
3560 )));
3561
3562 assert_eq!(
3563 serde_json::to_value(plan_update).unwrap(),
3564 json!({
3565 "sessionUpdate": "plan_update",
3566 "plan": {
3567 "type": "items",
3568 "planId": "plan-1",
3569 "entries": [
3570 {
3571 "content": "Step 1",
3572 "priority": "high",
3573 "status": "pending"
3574 }
3575 ]
3576 }
3577 })
3578 );
3579
3580 assert_eq!(
3581 serde_json::to_value(SessionUpdate::PlanRemoved(PlanRemoved::new("plan-1"))).unwrap(),
3582 json!({
3583 "sessionUpdate": "plan_removed",
3584 "planId": "plan-1"
3585 })
3586 );
3587
3588 let capabilities = ClientCapabilities::new().plan(PlanCapabilities::new());
3589 let json = serde_json::to_value(&capabilities).unwrap();
3590 assert_eq!(json["plan"], json!({}));
3591
3592 assert_eq!(
3593 serde_json::from_value::<ClientCapabilities>(json!({ "plan": null }))
3594 .unwrap()
3595 .plan,
3596 None
3597 );
3598 }
3599
3600 #[cfg(feature = "unstable_mcp_over_acp")]
3601 #[test]
3602 fn test_agent_mcp_request_method_names() {
3603 use serde_json::json;
3604
3605 let params: serde_json::Map<String, serde_json::Value> =
3606 [("cursor".to_string(), json!("abc"))].into_iter().collect();
3607
3608 assert_eq!(CLIENT_METHOD_NAMES.mcp_connect, "mcp/connect");
3609 assert_eq!(CLIENT_METHOD_NAMES.mcp_message, "mcp/message");
3610 assert_eq!(CLIENT_METHOD_NAMES.mcp_disconnect, "mcp/disconnect");
3611
3612 assert_eq!(
3613 AgentRequest::ConnectMcpRequest(ConnectMcpRequest::new("server-1")).method(),
3614 "mcp/connect"
3615 );
3616 assert_eq!(
3617 AgentRequest::MessageMcpRequest(MessageMcpRequest::new("conn-1", "tools/list"))
3618 .method(),
3619 "mcp/message"
3620 );
3621 assert_eq!(
3622 AgentRequest::DisconnectMcpRequest(DisconnectMcpRequest::new("conn-1")).method(),
3623 "mcp/disconnect"
3624 );
3625 assert_eq!(
3626 AgentNotification::MessageMcpNotification(MessageMcpNotification::new(
3627 "conn-1",
3628 "notifications/progress"
3629 ))
3630 .method(),
3631 "mcp/message"
3632 );
3633
3634 assert_eq!(
3635 serde_json::to_value(ConnectMcpRequest::new("server-1")).unwrap(),
3636 json!({ "serverId": "server-1" })
3637 );
3638 assert_eq!(
3639 serde_json::to_value(ConnectMcpResponse::new("conn-1")).unwrap(),
3640 json!({ "connectionId": "conn-1" })
3641 );
3642 assert_eq!(
3643 serde_json::to_value(MessageMcpRequest::new("conn-1", "tools/list").params(params))
3644 .unwrap(),
3645 json!({
3646 "connectionId": "conn-1",
3647 "method": "tools/list",
3648 "params": { "cursor": "abc" }
3649 })
3650 );
3651 assert_eq!(
3652 serde_json::to_value(DisconnectMcpRequest::new("conn-1")).unwrap(),
3653 json!({ "connectionId": "conn-1" })
3654 );
3655 assert_eq!(
3656 serde_json::to_value(MessageMcpNotification::new(
3657 "conn-1",
3658 "notifications/progress"
3659 ))
3660 .unwrap(),
3661 json!({
3662 "connectionId": "conn-1",
3663 "method": "notifications/progress"
3664 })
3665 );
3666
3667 let request_with_null_params: MessageMcpRequest = serde_json::from_value(json!({
3668 "connectionId": "conn-1",
3669 "method": "tools/list",
3670 "params": null
3671 }))
3672 .unwrap();
3673 assert_eq!(request_with_null_params.params, None);
3674 }
3675
3676 #[test]
3677 fn request_permission_request_rejects_malformed_options() {
3678 use serde_json::json;
3679
3680 assert!(
3681 serde_json::from_value::<RequestPermissionRequest>(json!({
3682 "sessionId": "sess-1",
3683 "toolCall": {"toolCallId": "tc-1"},
3684 "options": "not-an-array"
3685 }))
3686 .is_err()
3687 );
3688 assert!(
3689 serde_json::from_value::<RequestPermissionRequest>(json!({
3690 "sessionId": "sess-1",
3691 "toolCall": {"toolCallId": "tc-1"},
3692 "options": [{"optionId": "allow"}]
3693 }))
3694 .is_err()
3695 );
3696 }
3697}