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")]
208#[serde_as]
209#[skip_serializing_none]
210#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
211#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
212#[serde(rename_all = "camelCase")]
213#[non_exhaustive]
214pub struct Notice {
215 pub severity: NoticeSeverity,
217 #[cfg_attr(feature = "schemars", schemars(length(min = 1)))]
219 pub title: String,
220 #[serde_as(deserialize_as = "DefaultOnError")]
224 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
225 #[serde(default)]
226 pub description: Option<String>,
227 #[serde_as(deserialize_as = "DefaultOnError")]
231 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
232 #[serde(default, rename = "_meta")]
233 pub meta: Option<Meta>,
234}
235
236#[cfg(feature = "unstable_session_notices")]
237impl Notice {
238 #[must_use]
240 pub fn new(severity: NoticeSeverity, title: impl Into<String>) -> Self {
241 Self {
242 severity,
243 title: title.into(),
244 description: None,
245 meta: None,
246 }
247 }
248
249 #[must_use]
251 pub fn description(mut self, description: impl IntoOption<String>) -> Self {
252 self.description = description.into_option();
253 self
254 }
255
256 #[must_use]
258 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
259 self.meta = meta.into_option();
260 self
261 }
262}
263
264#[cfg(feature = "unstable_session_compaction")]
270#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
271#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
272#[serde(transparent)]
273#[from(Arc<str>, String, &'static str)]
274#[non_exhaustive]
275pub struct CompactionId(pub Arc<str>);
276
277#[cfg(feature = "unstable_session_compaction")]
278impl CompactionId {
279 #[must_use]
281 pub fn new(id: impl Into<Arc<str>>) -> Self {
282 Self(id.into())
283 }
284}
285
286#[cfg(feature = "unstable_session_compaction")]
292#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
293#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
294#[serde(rename_all = "snake_case")]
295#[non_exhaustive]
296pub enum CompactionStatus {
297 InProgress,
299 Completed,
301 Failed,
303 Cancelled,
305 #[serde(untagged)]
310 Other(String),
311}
312
313#[cfg(feature = "unstable_session_compaction")]
327#[serde_as]
328#[skip_serializing_none]
329#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
330#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
331#[serde(rename_all = "camelCase")]
332#[non_exhaustive]
333pub struct CompactionUpdate {
334 pub compaction_id: CompactionId,
336 pub status: CompactionStatus,
338 #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<VecSkipError<_, SkipListener>>>")]
340 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
341 #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
342 pub summary: MaybeUndefined<Vec<ContentBlock>>,
343 #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<_>>")]
345 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
346 #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
347 pub error: MaybeUndefined<String>,
348 #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<_>>")]
350 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
351 #[serde(
352 rename = "_meta",
353 default,
354 skip_serializing_if = "MaybeUndefined::is_undefined"
355 )]
356 pub meta: MaybeUndefined<Meta>,
357}
358
359#[cfg(feature = "unstable_session_compaction")]
360impl CompactionUpdate {
361 #[must_use]
363 pub fn new(compaction_id: impl Into<CompactionId>, status: CompactionStatus) -> Self {
364 Self {
365 compaction_id: compaction_id.into(),
366 status,
367 summary: MaybeUndefined::Undefined,
368 error: MaybeUndefined::Undefined,
369 meta: MaybeUndefined::Undefined,
370 }
371 }
372
373 #[must_use]
375 pub fn summary(mut self, summary: impl IntoMaybeUndefined<Vec<ContentBlock>>) -> Self {
376 self.summary = summary.into_maybe_undefined();
377 self
378 }
379
380 #[must_use]
382 pub fn error(mut self, error: impl IntoMaybeUndefined<String>) -> Self {
383 self.error = error.into_maybe_undefined();
384 self
385 }
386
387 #[must_use]
389 pub fn meta(mut self, meta: impl IntoMaybeUndefined<Meta>) -> Self {
390 self.meta = meta.into_maybe_undefined();
391 self
392 }
393}
394
395#[cfg(feature = "unstable_session_compaction")]
404#[serde_as]
405#[skip_serializing_none]
406#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
407#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
408#[serde(rename_all = "camelCase")]
409#[non_exhaustive]
410pub struct CompactionSummaryChunk {
411 pub compaction_id: CompactionId,
413 pub content: ContentBlock,
415 #[serde_as(deserialize_as = "DefaultOnError")]
417 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
418 #[serde(default, rename = "_meta")]
419 pub meta: Option<Meta>,
420}
421
422#[cfg(feature = "unstable_session_compaction")]
423impl CompactionSummaryChunk {
424 #[must_use]
426 pub fn new(compaction_id: impl Into<CompactionId>, content: ContentBlock) -> Self {
427 Self {
428 compaction_id: compaction_id.into(),
429 content,
430 meta: None,
431 }
432 }
433
434 #[must_use]
436 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
437 self.meta = meta.into_option();
438 self
439 }
440}
441
442#[serde_as]
446#[skip_serializing_none]
447#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
448#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
449#[serde(rename_all = "camelCase")]
450#[non_exhaustive]
451pub struct CurrentModeUpdate {
452 pub current_mode_id: SessionModeId,
454 #[serde_as(deserialize_as = "DefaultOnError")]
460 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
461 #[serde(default)]
462 #[serde(rename = "_meta")]
463 pub meta: Option<Meta>,
464}
465
466impl CurrentModeUpdate {
467 #[must_use]
469 pub fn new(current_mode_id: impl Into<SessionModeId>) -> Self {
470 Self {
471 current_mode_id: current_mode_id.into(),
472 meta: None,
473 }
474 }
475
476 #[must_use]
482 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
483 self.meta = meta.into_option();
484 self
485 }
486}
487
488#[serde_as]
490#[skip_serializing_none]
491#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
492#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
493#[serde(rename_all = "camelCase")]
494#[non_exhaustive]
495pub struct ConfigOptionUpdate {
496 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
498 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
499 pub config_options: Vec<SessionConfigOption>,
500 #[serde_as(deserialize_as = "DefaultOnError")]
506 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
507 #[serde(default)]
508 #[serde(rename = "_meta")]
509 pub meta: Option<Meta>,
510}
511
512impl ConfigOptionUpdate {
513 #[must_use]
515 pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
516 Self {
517 config_options,
518 meta: None,
519 }
520 }
521
522 #[must_use]
528 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
529 self.meta = meta.into_option();
530 self
531 }
532}
533
534#[serde_as]
539#[skip_serializing_none]
540#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
541#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
542#[serde(rename_all = "camelCase")]
543#[non_exhaustive]
544pub struct SessionInfoUpdate {
545 #[serde_as(deserialize_as = "DefaultOnError")]
547 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
548 #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
549 pub title: MaybeUndefined<String>,
550 #[serde_as(deserialize_as = "DefaultOnError")]
552 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
553 #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
554 pub updated_at: MaybeUndefined<String>,
555 #[serde_as(deserialize_as = "DefaultOnError")]
561 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
562 #[serde(default)]
563 #[serde(rename = "_meta")]
564 pub meta: Option<Meta>,
565}
566
567impl SessionInfoUpdate {
568 #[must_use]
570 pub fn new() -> Self {
571 Self::default()
572 }
573
574 #[must_use]
576 pub fn title(mut self, title: impl IntoMaybeUndefined<String>) -> Self {
577 self.title = title.into_maybe_undefined();
578 self
579 }
580
581 #[must_use]
583 pub fn updated_at(mut self, updated_at: impl IntoMaybeUndefined<String>) -> Self {
584 self.updated_at = updated_at.into_maybe_undefined();
585 self
586 }
587
588 #[must_use]
594 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
595 self.meta = meta.into_option();
596 self
597 }
598}
599
600#[serde_as]
602#[skip_serializing_none]
603#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
604#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
605#[serde(rename_all = "camelCase")]
606#[non_exhaustive]
607pub struct UsageUpdate {
608 pub used: u64,
610 pub size: u64,
612 #[serde_as(deserialize_as = "DefaultOnError")]
614 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
615 #[serde(default)]
616 pub cost: Option<Cost>,
617 #[serde_as(deserialize_as = "DefaultOnError")]
623 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
624 #[serde(default)]
625 #[serde(rename = "_meta")]
626 pub meta: Option<Meta>,
627}
628
629impl UsageUpdate {
630 #[must_use]
632 pub fn new(used: u64, size: u64) -> Self {
633 Self {
634 used,
635 size,
636 cost: None,
637 meta: None,
638 }
639 }
640
641 #[must_use]
643 pub fn cost(mut self, cost: impl IntoOption<Cost>) -> Self {
644 self.cost = cost.into_option();
645 self
646 }
647
648 #[must_use]
654 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
655 self.meta = meta.into_option();
656 self
657 }
658}
659
660#[serde_as]
662#[skip_serializing_none]
663#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
664#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
665#[serde(rename_all = "camelCase")]
666#[non_exhaustive]
667pub struct Cost {
668 pub amount: f64,
670 pub currency: String,
672 #[serde_as(deserialize_as = "DefaultOnError")]
678 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
679 #[serde(default)]
680 #[serde(rename = "_meta")]
681 pub meta: Option<Meta>,
682}
683
684impl Cost {
685 #[must_use]
687 pub fn new(amount: f64, currency: impl Into<String>) -> Self {
688 Self {
689 amount,
690 currency: currency.into(),
691 meta: None,
692 }
693 }
694
695 #[must_use]
701 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
702 self.meta = meta.into_option();
703 self
704 }
705}
706
707#[serde_as]
709#[skip_serializing_none]
710#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
711#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
712#[serde(rename_all = "camelCase")]
713#[non_exhaustive]
714pub struct ContentChunk {
715 pub content: ContentBlock,
717 #[serde_as(deserialize_as = "DefaultOnError")]
722 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
723 #[serde(default)]
724 pub message_id: Option<MessageId>,
725 #[serde_as(deserialize_as = "DefaultOnError")]
731 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
732 #[serde(default)]
733 #[serde(rename = "_meta")]
734 pub meta: Option<Meta>,
735}
736
737impl ContentChunk {
738 #[must_use]
740 pub fn new(content: ContentBlock) -> Self {
741 Self {
742 content,
743 message_id: None,
744 meta: None,
745 }
746 }
747
748 #[must_use]
753 pub fn message_id(mut self, message_id: impl IntoOption<MessageId>) -> Self {
754 self.message_id = message_id.into_option();
755 self
756 }
757
758 #[must_use]
764 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
765 self.meta = meta.into_option();
766 self
767 }
768}
769
770#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
772#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
773#[serde(transparent)]
774#[from(Arc<str>, String, &'static str)]
775#[non_exhaustive]
776pub struct MessageId(pub Arc<str>);
777
778impl MessageId {
779 #[must_use]
781 pub fn new(id: impl Into<Arc<str>>) -> Self {
782 Self(id.into())
783 }
784}
785
786impl IntoOption<MessageId> for &str {
787 fn into_option(self) -> Option<MessageId> {
788 Some(MessageId::new(self))
789 }
790}
791
792#[serde_as]
794#[skip_serializing_none]
795#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
796#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
797#[serde(rename_all = "camelCase")]
798#[non_exhaustive]
799pub struct AvailableCommandsUpdate {
800 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
802 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
803 pub available_commands: Vec<AvailableCommand>,
804 #[serde_as(deserialize_as = "DefaultOnError")]
810 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
811 #[serde(default)]
812 #[serde(rename = "_meta")]
813 pub meta: Option<Meta>,
814}
815
816impl AvailableCommandsUpdate {
817 #[must_use]
819 pub fn new(available_commands: Vec<AvailableCommand>) -> Self {
820 Self {
821 available_commands,
822 meta: None,
823 }
824 }
825
826 #[must_use]
832 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
833 self.meta = meta.into_option();
834 self
835 }
836}
837
838#[serde_as]
840#[skip_serializing_none]
841#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
842#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
843#[serde(rename_all = "camelCase")]
844#[non_exhaustive]
845pub struct AvailableCommand {
846 pub name: String,
848 pub description: String,
850 #[serde_as(deserialize_as = "DefaultOnError")]
852 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
853 #[serde(default)]
854 pub input: Option<AvailableCommandInput>,
855 #[serde_as(deserialize_as = "DefaultOnError")]
861 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
862 #[serde(default)]
863 #[serde(rename = "_meta")]
864 pub meta: Option<Meta>,
865}
866
867impl AvailableCommand {
868 #[must_use]
870 pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
871 Self {
872 name: name.into(),
873 description: description.into(),
874 input: None,
875 meta: None,
876 }
877 }
878
879 #[must_use]
881 pub fn input(mut self, input: impl IntoOption<AvailableCommandInput>) -> Self {
882 self.input = input.into_option();
883 self
884 }
885
886 #[must_use]
892 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
893 self.meta = meta.into_option();
894 self
895 }
896}
897
898#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
900#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
901#[serde(untagged, rename_all = "camelCase")]
902#[non_exhaustive]
903pub enum AvailableCommandInput {
904 Unstructured(UnstructuredCommandInput),
906}
907
908#[serde_as]
910#[skip_serializing_none]
911#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
912#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
913#[serde(rename_all = "camelCase")]
914#[non_exhaustive]
915pub struct UnstructuredCommandInput {
916 pub hint: String,
918 #[serde_as(deserialize_as = "DefaultOnError")]
924 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
925 #[serde(default)]
926 #[serde(rename = "_meta")]
927 pub meta: Option<Meta>,
928}
929
930impl UnstructuredCommandInput {
931 #[must_use]
933 pub fn new(hint: impl Into<String>) -> Self {
934 Self {
935 hint: hint.into(),
936 meta: None,
937 }
938 }
939
940 #[must_use]
946 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
947 self.meta = meta.into_option();
948 self
949 }
950}
951
952#[serde_as]
960#[skip_serializing_none]
961#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
962#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
963#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = SESSION_REQUEST_PERMISSION_METHOD_NAME)))]
964#[serde(rename_all = "camelCase")]
965#[non_exhaustive]
966pub struct RequestPermissionRequest {
967 pub session_id: SessionId,
969 pub tool_call: ToolCallUpdate,
971 pub options: Vec<PermissionOption>,
973 #[serde_as(deserialize_as = "DefaultOnError")]
979 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
980 #[serde(default)]
981 #[serde(rename = "_meta")]
982 pub meta: Option<Meta>,
983}
984
985impl RequestPermissionRequest {
986 #[must_use]
988 pub fn new(
989 session_id: impl Into<SessionId>,
990 tool_call: ToolCallUpdate,
991 options: Vec<PermissionOption>,
992 ) -> Self {
993 Self {
994 session_id: session_id.into(),
995 tool_call,
996 options,
997 meta: None,
998 }
999 }
1000
1001 #[must_use]
1007 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1008 self.meta = meta.into_option();
1009 self
1010 }
1011}
1012
1013#[serde_as]
1015#[skip_serializing_none]
1016#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1017#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1018#[serde(rename_all = "camelCase")]
1019#[non_exhaustive]
1020pub struct PermissionOption {
1021 pub option_id: PermissionOptionId,
1023 pub name: String,
1025 pub kind: PermissionOptionKind,
1027 #[serde_as(deserialize_as = "DefaultOnError")]
1033 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1034 #[serde(default)]
1035 #[serde(rename = "_meta")]
1036 pub meta: Option<Meta>,
1037}
1038
1039impl PermissionOption {
1040 #[must_use]
1042 pub fn new(
1043 option_id: impl Into<PermissionOptionId>,
1044 name: impl Into<String>,
1045 kind: PermissionOptionKind,
1046 ) -> Self {
1047 Self {
1048 option_id: option_id.into(),
1049 name: name.into(),
1050 kind,
1051 meta: None,
1052 }
1053 }
1054
1055 #[must_use]
1061 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1062 self.meta = meta.into_option();
1063 self
1064 }
1065}
1066
1067#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1069#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
1070#[serde(transparent)]
1071#[from(Arc<str>, String, &'static str)]
1072#[non_exhaustive]
1073pub struct PermissionOptionId(pub Arc<str>);
1074
1075impl PermissionOptionId {
1076 #[must_use]
1078 pub fn new(id: impl Into<Arc<str>>) -> Self {
1079 Self(id.into())
1080 }
1081}
1082
1083#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1087#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
1088#[serde(rename_all = "snake_case")]
1089#[non_exhaustive]
1090pub enum PermissionOptionKind {
1091 AllowOnce,
1093 AllowAlways,
1095 RejectOnce,
1097 RejectAlways,
1099}
1100
1101#[serde_as]
1103#[skip_serializing_none]
1104#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1105#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1106#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = SESSION_REQUEST_PERMISSION_METHOD_NAME)))]
1107#[serde(rename_all = "camelCase")]
1108#[non_exhaustive]
1109pub struct RequestPermissionResponse {
1110 pub outcome: RequestPermissionOutcome,
1113 #[serde_as(deserialize_as = "DefaultOnError")]
1119 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1120 #[serde(default)]
1121 #[serde(rename = "_meta")]
1122 pub meta: Option<Meta>,
1123}
1124
1125impl RequestPermissionResponse {
1126 #[must_use]
1128 pub fn new(outcome: RequestPermissionOutcome) -> Self {
1129 Self {
1130 outcome,
1131 meta: None,
1132 }
1133 }
1134
1135 #[must_use]
1141 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1142 self.meta = meta.into_option();
1143 self
1144 }
1145}
1146
1147#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1149#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1150#[serde(tag = "outcome", rename_all = "snake_case")]
1151#[cfg_attr(feature = "schemars", schemars(extend("discriminator" = {"propertyName": "outcome"})))]
1152#[non_exhaustive]
1153pub enum RequestPermissionOutcome {
1154 Cancelled,
1162 #[serde(rename_all = "camelCase")]
1164 Selected(SelectedPermissionOutcome),
1165}
1166
1167#[serde_as]
1169#[skip_serializing_none]
1170#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1171#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1172#[serde(rename_all = "camelCase")]
1173#[non_exhaustive]
1174pub struct SelectedPermissionOutcome {
1175 pub option_id: PermissionOptionId,
1177 #[serde_as(deserialize_as = "DefaultOnError")]
1183 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1184 #[serde(default)]
1185 #[serde(rename = "_meta")]
1186 pub meta: Option<Meta>,
1187}
1188
1189impl SelectedPermissionOutcome {
1190 #[must_use]
1192 pub fn new(option_id: impl Into<PermissionOptionId>) -> Self {
1193 Self {
1194 option_id: option_id.into(),
1195 meta: None,
1196 }
1197 }
1198
1199 #[must_use]
1205 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1206 self.meta = meta.into_option();
1207 self
1208 }
1209}
1210
1211#[serde_as]
1217#[skip_serializing_none]
1218#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1219#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1220#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = FS_WRITE_TEXT_FILE_METHOD_NAME)))]
1221#[serde(rename_all = "camelCase")]
1222#[non_exhaustive]
1223pub struct WriteTextFileRequest {
1224 pub session_id: SessionId,
1226 pub path: PathBuf,
1228 pub content: String,
1230 #[serde_as(deserialize_as = "DefaultOnError")]
1236 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1237 #[serde(default)]
1238 #[serde(rename = "_meta")]
1239 pub meta: Option<Meta>,
1240}
1241
1242impl WriteTextFileRequest {
1243 #[must_use]
1245 pub fn new(
1246 session_id: impl Into<SessionId>,
1247 path: impl Into<PathBuf>,
1248 content: impl Into<String>,
1249 ) -> Self {
1250 Self {
1251 session_id: session_id.into(),
1252 path: path.into(),
1253 content: content.into(),
1254 meta: None,
1255 }
1256 }
1257
1258 #[must_use]
1264 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1265 self.meta = meta.into_option();
1266 self
1267 }
1268}
1269
1270#[serde_as]
1272#[skip_serializing_none]
1273#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1274#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1275#[serde(rename_all = "camelCase")]
1276#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = FS_WRITE_TEXT_FILE_METHOD_NAME)))]
1277#[non_exhaustive]
1278pub struct WriteTextFileResponse {
1279 #[serde_as(deserialize_as = "DefaultOnError")]
1285 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1286 #[serde(default)]
1287 #[serde(rename = "_meta")]
1288 pub meta: Option<Meta>,
1289}
1290
1291impl WriteTextFileResponse {
1292 #[must_use]
1294 pub fn new() -> Self {
1295 Self::default()
1296 }
1297
1298 #[must_use]
1304 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1305 self.meta = meta.into_option();
1306 self
1307 }
1308}
1309
1310#[serde_as]
1316#[skip_serializing_none]
1317#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1318#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1319#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = FS_READ_TEXT_FILE_METHOD_NAME)))]
1320#[serde(rename_all = "camelCase")]
1321#[non_exhaustive]
1322pub struct ReadTextFileRequest {
1323 pub session_id: SessionId,
1325 pub path: PathBuf,
1327 #[serde_as(deserialize_as = "DefaultOnError")]
1329 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1330 #[serde(default)]
1331 pub line: Option<u32>,
1332 #[serde_as(deserialize_as = "DefaultOnError")]
1334 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1335 #[serde(default)]
1336 pub limit: Option<u32>,
1337 #[serde_as(deserialize_as = "DefaultOnError")]
1343 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1344 #[serde(default)]
1345 #[serde(rename = "_meta")]
1346 pub meta: Option<Meta>,
1347}
1348
1349impl ReadTextFileRequest {
1350 #[must_use]
1352 pub fn new(session_id: impl Into<SessionId>, path: impl Into<PathBuf>) -> Self {
1353 Self {
1354 session_id: session_id.into(),
1355 path: path.into(),
1356 line: None,
1357 limit: None,
1358 meta: None,
1359 }
1360 }
1361
1362 #[must_use]
1364 pub fn line(mut self, line: impl IntoOption<u32>) -> Self {
1365 self.line = line.into_option();
1366 self
1367 }
1368
1369 #[must_use]
1371 pub fn limit(mut self, limit: impl IntoOption<u32>) -> Self {
1372 self.limit = limit.into_option();
1373 self
1374 }
1375
1376 #[must_use]
1382 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1383 self.meta = meta.into_option();
1384 self
1385 }
1386}
1387
1388#[serde_as]
1390#[skip_serializing_none]
1391#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1392#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1393#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = FS_READ_TEXT_FILE_METHOD_NAME)))]
1394#[serde(rename_all = "camelCase")]
1395#[non_exhaustive]
1396pub struct ReadTextFileResponse {
1397 pub content: String,
1399 #[serde_as(deserialize_as = "DefaultOnError")]
1405 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1406 #[serde(default)]
1407 #[serde(rename = "_meta")]
1408 pub meta: Option<Meta>,
1409}
1410
1411impl ReadTextFileResponse {
1412 #[must_use]
1414 pub fn new(content: impl Into<String>) -> Self {
1415 Self {
1416 content: content.into(),
1417 meta: None,
1418 }
1419 }
1420
1421 #[must_use]
1427 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1428 self.meta = meta.into_option();
1429 self
1430 }
1431}
1432
1433#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1437#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
1438#[serde(transparent)]
1439#[from(Arc<str>, String, &'static str)]
1440#[non_exhaustive]
1441pub struct TerminalId(pub Arc<str>);
1442
1443impl TerminalId {
1444 #[must_use]
1446 pub fn new(id: impl Into<Arc<str>>) -> Self {
1447 Self(id.into())
1448 }
1449}
1450
1451#[serde_as]
1453#[skip_serializing_none]
1454#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1455#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1456#[serde(rename_all = "camelCase")]
1457#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_CREATE_METHOD_NAME)))]
1458#[non_exhaustive]
1459pub struct CreateTerminalRequest {
1460 pub session_id: SessionId,
1462 pub command: String,
1464 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1466 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1467 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1468 pub args: Vec<String>,
1469 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1471 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1472 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1473 pub env: Vec<EnvVariable>,
1474 #[serde_as(deserialize_as = "DefaultOnError")]
1476 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1477 #[serde(default)]
1478 pub cwd: Option<PathBuf>,
1479 #[serde_as(deserialize_as = "DefaultOnError")]
1488 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1489 #[serde(default)]
1490 pub output_byte_limit: Option<u64>,
1491 #[serde_as(deserialize_as = "DefaultOnError")]
1497 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1498 #[serde(default)]
1499 #[serde(rename = "_meta")]
1500 pub meta: Option<Meta>,
1501}
1502
1503impl CreateTerminalRequest {
1504 #[must_use]
1506 pub fn new(session_id: impl Into<SessionId>, command: impl Into<String>) -> Self {
1507 Self {
1508 session_id: session_id.into(),
1509 command: command.into(),
1510 args: Vec::new(),
1511 env: Vec::new(),
1512 cwd: None,
1513 output_byte_limit: None,
1514 meta: None,
1515 }
1516 }
1517
1518 #[must_use]
1520 pub fn args(mut self, args: Vec<String>) -> Self {
1521 self.args = args;
1522 self
1523 }
1524
1525 #[must_use]
1527 pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
1528 self.env = env;
1529 self
1530 }
1531
1532 #[must_use]
1534 pub fn cwd(mut self, cwd: impl IntoOption<PathBuf>) -> Self {
1535 self.cwd = cwd.into_option();
1536 self
1537 }
1538
1539 #[must_use]
1548 pub fn output_byte_limit(mut self, output_byte_limit: impl IntoOption<u64>) -> Self {
1549 self.output_byte_limit = output_byte_limit.into_option();
1550 self
1551 }
1552
1553 #[must_use]
1559 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1560 self.meta = meta.into_option();
1561 self
1562 }
1563}
1564
1565#[serde_as]
1567#[skip_serializing_none]
1568#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1569#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1570#[serde(rename_all = "camelCase")]
1571#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_CREATE_METHOD_NAME)))]
1572#[non_exhaustive]
1573pub struct CreateTerminalResponse {
1574 pub terminal_id: TerminalId,
1576 #[serde_as(deserialize_as = "DefaultOnError")]
1582 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1583 #[serde(default)]
1584 #[serde(rename = "_meta")]
1585 pub meta: Option<Meta>,
1586}
1587
1588impl CreateTerminalResponse {
1589 #[must_use]
1591 pub fn new(terminal_id: impl Into<TerminalId>) -> Self {
1592 Self {
1593 terminal_id: terminal_id.into(),
1594 meta: None,
1595 }
1596 }
1597
1598 #[must_use]
1604 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1605 self.meta = meta.into_option();
1606 self
1607 }
1608}
1609
1610#[serde_as]
1612#[skip_serializing_none]
1613#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1614#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1615#[serde(rename_all = "camelCase")]
1616#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_OUTPUT_METHOD_NAME)))]
1617#[non_exhaustive]
1618pub struct TerminalOutputRequest {
1619 pub session_id: SessionId,
1621 pub terminal_id: TerminalId,
1623 #[serde_as(deserialize_as = "DefaultOnError")]
1629 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1630 #[serde(default)]
1631 #[serde(rename = "_meta")]
1632 pub meta: Option<Meta>,
1633}
1634
1635impl TerminalOutputRequest {
1636 #[must_use]
1638 pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1639 Self {
1640 session_id: session_id.into(),
1641 terminal_id: terminal_id.into(),
1642 meta: None,
1643 }
1644 }
1645
1646 #[must_use]
1652 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1653 self.meta = meta.into_option();
1654 self
1655 }
1656}
1657
1658#[serde_as]
1660#[skip_serializing_none]
1661#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1662#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1663#[serde(rename_all = "camelCase")]
1664#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_OUTPUT_METHOD_NAME)))]
1665#[non_exhaustive]
1666pub struct TerminalOutputResponse {
1667 pub output: String,
1669 pub truncated: bool,
1671 #[serde_as(deserialize_as = "DefaultOnError")]
1673 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1674 #[serde(default)]
1675 pub exit_status: Option<TerminalExitStatus>,
1676 #[serde_as(deserialize_as = "DefaultOnError")]
1682 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1683 #[serde(default)]
1684 #[serde(rename = "_meta")]
1685 pub meta: Option<Meta>,
1686}
1687
1688impl TerminalOutputResponse {
1689 #[must_use]
1691 pub fn new(output: impl Into<String>, truncated: bool) -> Self {
1692 Self {
1693 output: output.into(),
1694 truncated,
1695 exit_status: None,
1696 meta: None,
1697 }
1698 }
1699
1700 #[must_use]
1702 pub fn exit_status(mut self, exit_status: impl IntoOption<TerminalExitStatus>) -> Self {
1703 self.exit_status = exit_status.into_option();
1704 self
1705 }
1706
1707 #[must_use]
1713 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1714 self.meta = meta.into_option();
1715 self
1716 }
1717}
1718
1719#[serde_as]
1721#[skip_serializing_none]
1722#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1723#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1724#[serde(rename_all = "camelCase")]
1725#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_RELEASE_METHOD_NAME)))]
1726#[non_exhaustive]
1727pub struct ReleaseTerminalRequest {
1728 pub session_id: SessionId,
1730 pub terminal_id: TerminalId,
1732 #[serde_as(deserialize_as = "DefaultOnError")]
1738 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1739 #[serde(default)]
1740 #[serde(rename = "_meta")]
1741 pub meta: Option<Meta>,
1742}
1743
1744impl ReleaseTerminalRequest {
1745 #[must_use]
1747 pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1748 Self {
1749 session_id: session_id.into(),
1750 terminal_id: terminal_id.into(),
1751 meta: None,
1752 }
1753 }
1754
1755 #[must_use]
1761 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1762 self.meta = meta.into_option();
1763 self
1764 }
1765}
1766
1767#[serde_as]
1769#[skip_serializing_none]
1770#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1771#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1772#[serde(rename_all = "camelCase")]
1773#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_RELEASE_METHOD_NAME)))]
1774#[non_exhaustive]
1775pub struct ReleaseTerminalResponse {
1776 #[serde_as(deserialize_as = "DefaultOnError")]
1782 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1783 #[serde(default)]
1784 #[serde(rename = "_meta")]
1785 pub meta: Option<Meta>,
1786}
1787
1788impl ReleaseTerminalResponse {
1789 #[must_use]
1791 pub fn new() -> Self {
1792 Self::default()
1793 }
1794
1795 #[must_use]
1801 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1802 self.meta = meta.into_option();
1803 self
1804 }
1805}
1806
1807#[serde_as]
1809#[skip_serializing_none]
1810#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1811#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1812#[serde(rename_all = "camelCase")]
1813#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_KILL_METHOD_NAME)))]
1814#[non_exhaustive]
1815pub struct KillTerminalRequest {
1816 pub session_id: SessionId,
1818 pub terminal_id: TerminalId,
1820 #[serde_as(deserialize_as = "DefaultOnError")]
1826 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1827 #[serde(default)]
1828 #[serde(rename = "_meta")]
1829 pub meta: Option<Meta>,
1830}
1831
1832impl KillTerminalRequest {
1833 #[must_use]
1835 pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1836 Self {
1837 session_id: session_id.into(),
1838 terminal_id: terminal_id.into(),
1839 meta: None,
1840 }
1841 }
1842
1843 #[must_use]
1849 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1850 self.meta = meta.into_option();
1851 self
1852 }
1853}
1854
1855#[serde_as]
1857#[skip_serializing_none]
1858#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1859#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1860#[serde(rename_all = "camelCase")]
1861#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_KILL_METHOD_NAME)))]
1862#[non_exhaustive]
1863pub struct KillTerminalResponse {
1864 #[serde_as(deserialize_as = "DefaultOnError")]
1870 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1871 #[serde(default)]
1872 #[serde(rename = "_meta")]
1873 pub meta: Option<Meta>,
1874}
1875
1876impl KillTerminalResponse {
1877 #[must_use]
1879 pub fn new() -> Self {
1880 Self::default()
1881 }
1882
1883 #[must_use]
1889 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1890 self.meta = meta.into_option();
1891 self
1892 }
1893}
1894
1895#[serde_as]
1897#[skip_serializing_none]
1898#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1899#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1900#[serde(rename_all = "camelCase")]
1901#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_WAIT_FOR_EXIT_METHOD_NAME)))]
1902#[non_exhaustive]
1903pub struct WaitForTerminalExitRequest {
1904 pub session_id: SessionId,
1906 pub terminal_id: TerminalId,
1908 #[serde_as(deserialize_as = "DefaultOnError")]
1914 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1915 #[serde(default)]
1916 #[serde(rename = "_meta")]
1917 pub meta: Option<Meta>,
1918}
1919
1920impl WaitForTerminalExitRequest {
1921 #[must_use]
1923 pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1924 Self {
1925 session_id: session_id.into(),
1926 terminal_id: terminal_id.into(),
1927 meta: None,
1928 }
1929 }
1930
1931 #[must_use]
1937 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1938 self.meta = meta.into_option();
1939 self
1940 }
1941}
1942
1943#[serde_as]
1945#[skip_serializing_none]
1946#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1947#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1948#[serde(rename_all = "camelCase")]
1949#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_WAIT_FOR_EXIT_METHOD_NAME)))]
1950#[non_exhaustive]
1951pub struct WaitForTerminalExitResponse {
1952 #[serde(flatten)]
1954 pub exit_status: TerminalExitStatus,
1955 #[serde_as(deserialize_as = "DefaultOnError")]
1961 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1962 #[serde(default)]
1963 #[serde(rename = "_meta")]
1964 pub meta: Option<Meta>,
1965}
1966
1967impl WaitForTerminalExitResponse {
1968 #[must_use]
1970 pub fn new(exit_status: TerminalExitStatus) -> Self {
1971 Self {
1972 exit_status,
1973 meta: None,
1974 }
1975 }
1976
1977 #[must_use]
1983 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1984 self.meta = meta.into_option();
1985 self
1986 }
1987}
1988
1989#[serde_as]
1991#[skip_serializing_none]
1992#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1993#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1994#[serde(rename_all = "camelCase")]
1995#[non_exhaustive]
1996pub struct TerminalExitStatus {
1997 #[serde_as(deserialize_as = "DefaultOnError")]
1999 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2000 #[serde(default)]
2001 pub exit_code: Option<u32>,
2002 #[serde_as(deserialize_as = "DefaultOnError")]
2004 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2005 #[serde(default)]
2006 pub signal: Option<String>,
2007 #[serde_as(deserialize_as = "DefaultOnError")]
2013 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2014 #[serde(default)]
2015 #[serde(rename = "_meta")]
2016 pub meta: Option<Meta>,
2017}
2018
2019impl TerminalExitStatus {
2020 #[must_use]
2022 pub fn new() -> Self {
2023 Self::default()
2024 }
2025
2026 #[must_use]
2028 pub fn exit_code(mut self, exit_code: impl IntoOption<u32>) -> Self {
2029 self.exit_code = exit_code.into_option();
2030 self
2031 }
2032
2033 #[must_use]
2035 pub fn signal(mut self, signal: impl IntoOption<String>) -> Self {
2036 self.signal = signal.into_option();
2037 self
2038 }
2039
2040 #[must_use]
2046 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2047 self.meta = meta.into_option();
2048 self
2049 }
2050}
2051
2052#[serde_as]
2061#[skip_serializing_none]
2062#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2063#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2064#[serde(rename_all = "camelCase")]
2065#[non_exhaustive]
2066pub struct ClientCapabilities {
2067 #[serde_as(deserialize_as = "DefaultOnError")]
2070 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2071 #[serde(default)]
2072 pub fs: FileSystemCapabilities,
2073 #[serde_as(deserialize_as = "DefaultOnError")]
2075 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2076 #[serde(default)]
2077 pub terminal: bool,
2078 #[serde_as(deserialize_as = "DefaultOnError")]
2083 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2084 #[serde(default)]
2085 pub session: Option<ClientSessionCapabilities>,
2086 #[cfg(feature = "unstable_plan_operations")]
2095 #[serde_as(deserialize_as = "DefaultOnError")]
2096 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2097 #[serde(default)]
2098 pub plan: Option<PlanCapabilities>,
2099 #[serde_as(deserialize_as = "DefaultOnError")]
2103 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2104 #[serde(default)]
2105 pub auth: AuthCapabilities,
2106 #[serde_as(deserialize_as = "DefaultOnError")]
2112 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2113 #[serde(default)]
2114 pub elicitation: Option<ElicitationCapabilities>,
2115 #[cfg(feature = "unstable_nes")]
2124 #[serde_as(deserialize_as = "DefaultOnError")]
2125 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2126 #[serde(default)]
2127 pub nes: Option<ClientNesCapabilities>,
2128 #[cfg(feature = "unstable_nes")]
2134 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
2135 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2136 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2137 pub position_encodings: Vec<PositionEncodingKind>,
2138
2139 #[serde_as(deserialize_as = "DefaultOnError")]
2145 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2146 #[serde(default)]
2147 #[serde(rename = "_meta")]
2148 pub meta: Option<Meta>,
2149}
2150
2151impl ClientCapabilities {
2152 #[must_use]
2154 pub fn new() -> Self {
2155 Self::default()
2156 }
2157
2158 #[must_use]
2161 pub fn fs(mut self, fs: FileSystemCapabilities) -> Self {
2162 self.fs = fs;
2163 self
2164 }
2165
2166 #[must_use]
2168 pub fn terminal(mut self, terminal: bool) -> Self {
2169 self.terminal = terminal;
2170 self
2171 }
2172
2173 #[must_use]
2175 pub fn session(mut self, session: impl IntoOption<ClientSessionCapabilities>) -> Self {
2176 self.session = session.into_option();
2177 self
2178 }
2179
2180 #[cfg(feature = "unstable_plan_operations")]
2189 #[must_use]
2190 pub fn plan(mut self, plan: impl IntoOption<PlanCapabilities>) -> Self {
2191 self.plan = plan.into_option();
2192 self
2193 }
2194
2195 #[must_use]
2199 pub fn auth(mut self, auth: AuthCapabilities) -> Self {
2200 self.auth = auth;
2201 self
2202 }
2203
2204 #[must_use]
2207 pub fn elicitation(mut self, elicitation: impl IntoOption<ElicitationCapabilities>) -> Self {
2208 self.elicitation = elicitation.into_option();
2209 self
2210 }
2211
2212 #[cfg(feature = "unstable_nes")]
2216 #[must_use]
2217 pub fn nes(mut self, nes: impl IntoOption<ClientNesCapabilities>) -> Self {
2218 self.nes = nes.into_option();
2219 self
2220 }
2221
2222 #[cfg(feature = "unstable_nes")]
2226 #[must_use]
2227 pub fn position_encodings(mut self, position_encodings: Vec<PositionEncodingKind>) -> Self {
2228 self.position_encodings = position_encodings;
2229 self
2230 }
2231
2232 #[must_use]
2238 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2239 self.meta = meta.into_option();
2240 self
2241 }
2242}
2243
2244#[serde_as]
2246#[skip_serializing_none]
2247#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2248#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2249#[serde(rename_all = "camelCase")]
2250#[non_exhaustive]
2251pub struct ClientSessionCapabilities {
2252 #[cfg(feature = "unstable_session_compaction")]
2259 #[serde_as(deserialize_as = "DefaultOnError")]
2260 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2261 #[serde(default)]
2262 pub compaction: Option<CompactionCapabilities>,
2263 #[serde_as(deserialize_as = "DefaultOnError")]
2268 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2269 #[serde(default)]
2270 pub config_options: Option<SessionConfigOptionsCapabilities>,
2271 #[serde_as(deserialize_as = "DefaultOnError")]
2277 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2278 #[serde(default)]
2279 #[serde(rename = "_meta")]
2280 pub meta: Option<Meta>,
2281}
2282
2283impl ClientSessionCapabilities {
2284 #[must_use]
2286 pub fn new() -> Self {
2287 Self::default()
2288 }
2289
2290 #[cfg(feature = "unstable_session_compaction")]
2292 #[must_use]
2293 pub fn compaction(mut self, compaction: impl IntoOption<CompactionCapabilities>) -> Self {
2294 self.compaction = compaction.into_option();
2295 self
2296 }
2297
2298 #[must_use]
2303 pub fn config_options(
2304 mut self,
2305 config_options: impl IntoOption<SessionConfigOptionsCapabilities>,
2306 ) -> Self {
2307 self.config_options = config_options.into_option();
2308 self
2309 }
2310
2311 #[must_use]
2317 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2318 self.meta = meta.into_option();
2319 self
2320 }
2321}
2322
2323#[cfg(feature = "unstable_session_compaction")]
2329#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2330#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2331#[serde(rename_all = "camelCase")]
2332#[non_exhaustive]
2333pub struct CompactionCapabilities {}
2334
2335#[cfg(feature = "unstable_session_compaction")]
2336impl CompactionCapabilities {
2337 #[must_use]
2339 pub fn new() -> Self {
2340 Self {}
2341 }
2342}
2343
2344#[serde_as]
2346#[skip_serializing_none]
2347#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2348#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2349#[serde(rename_all = "camelCase")]
2350#[non_exhaustive]
2351pub struct SessionConfigOptionsCapabilities {
2352 #[serde_as(deserialize_as = "DefaultOnError")]
2359 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2360 #[serde(default)]
2361 pub boolean: Option<BooleanConfigOptionCapabilities>,
2362 #[serde_as(deserialize_as = "DefaultOnError")]
2368 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2369 #[serde(default)]
2370 #[serde(rename = "_meta")]
2371 pub meta: Option<Meta>,
2372}
2373
2374impl SessionConfigOptionsCapabilities {
2375 #[must_use]
2377 pub fn new() -> Self {
2378 Self::default()
2379 }
2380
2381 #[must_use]
2388 pub fn boolean(mut self, boolean: impl IntoOption<BooleanConfigOptionCapabilities>) -> Self {
2389 self.boolean = boolean.into_option();
2390 self
2391 }
2392
2393 #[must_use]
2399 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2400 self.meta = meta.into_option();
2401 self
2402 }
2403}
2404
2405#[serde_as]
2409#[skip_serializing_none]
2410#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2411#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2412#[non_exhaustive]
2413pub struct 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 BooleanConfigOptionCapabilities {
2427 #[must_use]
2429 pub fn new() -> Self {
2430 Self::default()
2431 }
2432
2433 #[must_use]
2439 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2440 self.meta = meta.into_option();
2441 self
2442 }
2443}
2444
2445#[serde_as]
2451#[skip_serializing_none]
2452#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2453#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2454#[serde(rename_all = "camelCase")]
2455#[non_exhaustive]
2456pub struct AuthCapabilities {
2457 #[serde_as(deserialize_as = "DefaultOnError")]
2463 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2464 #[serde(default)]
2465 pub terminal: bool,
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 AuthCapabilities {
2479 #[must_use]
2481 pub fn new() -> Self {
2482 Self::default()
2483 }
2484
2485 #[must_use]
2492 pub fn terminal(mut self, terminal: bool) -> Self {
2493 self.terminal = terminal;
2494 self
2495 }
2496
2497 #[must_use]
2503 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2504 self.meta = meta.into_option();
2505 self
2506 }
2507}
2508
2509#[serde_as]
2513#[skip_serializing_none]
2514#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2515#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2516#[serde(rename_all = "camelCase")]
2517#[non_exhaustive]
2518pub struct FileSystemCapabilities {
2519 #[serde_as(deserialize_as = "DefaultOnError")]
2521 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2522 #[serde(default)]
2523 pub read_text_file: bool,
2524 #[serde_as(deserialize_as = "DefaultOnError")]
2526 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2527 #[serde(default)]
2528 pub write_text_file: bool,
2529 #[serde_as(deserialize_as = "DefaultOnError")]
2535 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2536 #[serde(default)]
2537 #[serde(rename = "_meta")]
2538 pub meta: Option<Meta>,
2539}
2540
2541impl FileSystemCapabilities {
2542 #[must_use]
2544 pub fn new() -> Self {
2545 Self::default()
2546 }
2547
2548 #[must_use]
2550 pub fn read_text_file(mut self, read_text_file: bool) -> Self {
2551 self.read_text_file = read_text_file;
2552 self
2553 }
2554
2555 #[must_use]
2557 pub fn write_text_file(mut self, write_text_file: bool) -> Self {
2558 self.write_text_file = write_text_file;
2559 self
2560 }
2561
2562 #[must_use]
2568 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2569 self.meta = meta.into_option();
2570 self
2571 }
2572}
2573
2574#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2580#[non_exhaustive]
2581pub struct ClientMethodNames {
2582 pub session_request_permission: &'static str,
2584 pub session_update: &'static str,
2586 pub fs_write_text_file: &'static str,
2588 pub fs_read_text_file: &'static str,
2590 pub terminal_create: &'static str,
2592 pub terminal_output: &'static str,
2594 pub terminal_release: &'static str,
2596 pub terminal_wait_for_exit: &'static str,
2598 pub terminal_kill: &'static str,
2600 #[cfg(feature = "unstable_mcp_over_acp")]
2602 pub mcp_connect: &'static str,
2603 #[cfg(feature = "unstable_mcp_over_acp")]
2605 pub mcp_message: &'static str,
2606 #[cfg(feature = "unstable_mcp_over_acp")]
2608 pub mcp_disconnect: &'static str,
2609 pub elicitation_create: &'static str,
2611 pub elicitation_complete: &'static str,
2613}
2614
2615pub const CLIENT_METHOD_NAMES: ClientMethodNames = ClientMethodNames {
2617 session_update: SESSION_UPDATE_NOTIFICATION,
2618 session_request_permission: SESSION_REQUEST_PERMISSION_METHOD_NAME,
2619 fs_write_text_file: FS_WRITE_TEXT_FILE_METHOD_NAME,
2620 fs_read_text_file: FS_READ_TEXT_FILE_METHOD_NAME,
2621 terminal_create: TERMINAL_CREATE_METHOD_NAME,
2622 terminal_output: TERMINAL_OUTPUT_METHOD_NAME,
2623 terminal_release: TERMINAL_RELEASE_METHOD_NAME,
2624 terminal_wait_for_exit: TERMINAL_WAIT_FOR_EXIT_METHOD_NAME,
2625 terminal_kill: TERMINAL_KILL_METHOD_NAME,
2626 #[cfg(feature = "unstable_mcp_over_acp")]
2627 mcp_connect: MCP_CONNECT_METHOD_NAME,
2628 #[cfg(feature = "unstable_mcp_over_acp")]
2629 mcp_message: MCP_MESSAGE_METHOD_NAME,
2630 #[cfg(feature = "unstable_mcp_over_acp")]
2631 mcp_disconnect: MCP_DISCONNECT_METHOD_NAME,
2632 elicitation_create: ELICITATION_CREATE_METHOD_NAME,
2633 elicitation_complete: ELICITATION_COMPLETE_NOTIFICATION,
2634};
2635
2636pub(crate) const SESSION_UPDATE_NOTIFICATION: &str = "session/update";
2638pub(crate) const SESSION_REQUEST_PERMISSION_METHOD_NAME: &str = "session/request_permission";
2640pub(crate) const FS_WRITE_TEXT_FILE_METHOD_NAME: &str = "fs/write_text_file";
2642pub(crate) const FS_READ_TEXT_FILE_METHOD_NAME: &str = "fs/read_text_file";
2644pub(crate) const TERMINAL_CREATE_METHOD_NAME: &str = "terminal/create";
2646pub(crate) const TERMINAL_OUTPUT_METHOD_NAME: &str = "terminal/output";
2648pub(crate) const TERMINAL_RELEASE_METHOD_NAME: &str = "terminal/release";
2650pub(crate) const TERMINAL_WAIT_FOR_EXIT_METHOD_NAME: &str = "terminal/wait_for_exit";
2652pub(crate) const TERMINAL_KILL_METHOD_NAME: &str = "terminal/kill";
2654pub(crate) const ELICITATION_CREATE_METHOD_NAME: &str = "elicitation/create";
2656pub(crate) const ELICITATION_COMPLETE_NOTIFICATION: &str = "elicitation/complete";
2658
2659#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2666#[derive(Clone, Debug, Serialize, Deserialize)]
2667#[serde(untagged)]
2668#[cfg_attr(feature = "schemars", schemars(inline))]
2669#[non_exhaustive]
2670#[allow(clippy::large_enum_variant)]
2671pub enum AgentRequest {
2672 WriteTextFileRequest(WriteTextFileRequest),
2679 ReadTextFileRequest(ReadTextFileRequest),
2686 RequestPermissionRequest(RequestPermissionRequest),
2697 CreateTerminalRequest(CreateTerminalRequest),
2712 TerminalOutputRequest(TerminalOutputRequest),
2719 ReleaseTerminalRequest(ReleaseTerminalRequest),
2732 WaitForTerminalExitRequest(WaitForTerminalExitRequest),
2736 KillTerminalRequest(KillTerminalRequest),
2749 CreateElicitationRequest(CreateElicitationRequest),
2753 #[cfg(feature = "unstable_mcp_over_acp")]
2759 ConnectMcpRequest(ConnectMcpRequest),
2760 #[cfg(feature = "unstable_mcp_over_acp")]
2766 MessageMcpRequest(MessageMcpRequest),
2767 #[cfg(feature = "unstable_mcp_over_acp")]
2773 DisconnectMcpRequest(DisconnectMcpRequest),
2774 ExtMethodRequest(ExtRequest),
2782}
2783
2784impl AgentRequest {
2785 #[must_use]
2787 pub fn method(&self) -> &str {
2788 match self {
2789 Self::WriteTextFileRequest(_) => CLIENT_METHOD_NAMES.fs_write_text_file,
2790 Self::ReadTextFileRequest(_) => CLIENT_METHOD_NAMES.fs_read_text_file,
2791 Self::RequestPermissionRequest(_) => CLIENT_METHOD_NAMES.session_request_permission,
2792 Self::CreateTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_create,
2793 Self::TerminalOutputRequest(_) => CLIENT_METHOD_NAMES.terminal_output,
2794 Self::ReleaseTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_release,
2795 Self::WaitForTerminalExitRequest(_) => CLIENT_METHOD_NAMES.terminal_wait_for_exit,
2796 Self::KillTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_kill,
2797 Self::CreateElicitationRequest(_) => CLIENT_METHOD_NAMES.elicitation_create,
2798 #[cfg(feature = "unstable_mcp_over_acp")]
2799 Self::ConnectMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_connect,
2800 #[cfg(feature = "unstable_mcp_over_acp")]
2801 Self::MessageMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_message,
2802 #[cfg(feature = "unstable_mcp_over_acp")]
2803 Self::DisconnectMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_disconnect,
2804 Self::ExtMethodRequest(ext_request) => &ext_request.method,
2805 }
2806 }
2807}
2808
2809#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2816#[derive(Clone, Debug, Serialize, Deserialize)]
2817#[serde(untagged)]
2818#[cfg_attr(feature = "schemars", schemars(inline))]
2819#[non_exhaustive]
2820pub enum ClientResponse {
2821 WriteTextFileResponse(#[serde(default)] WriteTextFileResponse),
2823 ReadTextFileResponse(ReadTextFileResponse),
2825 RequestPermissionResponse(RequestPermissionResponse),
2827 CreateTerminalResponse(CreateTerminalResponse),
2829 TerminalOutputResponse(TerminalOutputResponse),
2831 ReleaseTerminalResponse(#[serde(default)] ReleaseTerminalResponse),
2833 WaitForTerminalExitResponse(WaitForTerminalExitResponse),
2835 KillTerminalResponse(#[serde(default)] KillTerminalResponse),
2837 CreateElicitationResponse(CreateElicitationResponse),
2839 #[cfg(feature = "unstable_mcp_over_acp")]
2841 ConnectMcpResponse(ConnectMcpResponse),
2842 #[cfg(feature = "unstable_mcp_over_acp")]
2844 DisconnectMcpResponse(#[serde(default)] DisconnectMcpResponse),
2845 #[cfg(feature = "unstable_mcp_over_acp")]
2847 MessageMcpResponse(MessageMcpResponse),
2848 ExtMethodResponse(ExtResponse),
2850}
2851
2852#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2859#[derive(Clone, Debug, Serialize, Deserialize)]
2860#[serde(untagged)]
2861#[expect(clippy::large_enum_variant)]
2862#[cfg_attr(feature = "schemars", schemars(inline))]
2863#[non_exhaustive]
2864pub enum AgentNotification {
2865 SessionNotification(SessionNotification),
2877 CompleteElicitationNotification(CompleteElicitationNotification),
2881 #[cfg(feature = "unstable_mcp_over_acp")]
2887 MessageMcpNotification(MessageMcpNotification),
2888 ExtNotification(ExtNotification),
2896}
2897
2898impl AgentNotification {
2899 #[must_use]
2901 pub fn method(&self) -> &str {
2902 match self {
2903 Self::SessionNotification(_) => CLIENT_METHOD_NAMES.session_update,
2904 Self::CompleteElicitationNotification(_) => CLIENT_METHOD_NAMES.elicitation_complete,
2905 #[cfg(feature = "unstable_mcp_over_acp")]
2906 Self::MessageMcpNotification(_) => CLIENT_METHOD_NAMES.mcp_message,
2907 Self::ExtNotification(ext_notification) => &ext_notification.method,
2908 }
2909 }
2910}
2911
2912#[cfg(test)]
2913mod tests {
2914 use super::*;
2915
2916 #[cfg(feature = "unstable_session_notices")]
2917 #[test]
2918 fn notice_preserves_wire_shape_nullable_fields_and_open_severity() {
2919 use serde_json::json;
2920
2921 let mut meta = Meta::new();
2922 meta.insert("source".into(), json!("fallback"));
2923 assert_eq!(
2924 serde_json::to_value(SessionUpdate::Notice(
2925 Notice::new(NoticeSeverity::Warning, "MCP server unavailable")
2926 .description("Continuing without it.")
2927 .meta(meta),
2928 ))
2929 .unwrap(),
2930 json!({
2931 "sessionUpdate": "notice",
2932 "severity": "warning",
2933 "title": "MCP server unavailable",
2934 "description": "Continuing without it.",
2935 "_meta": { "source": "fallback" }
2936 })
2937 );
2938
2939 assert_eq!(
2940 serde_json::to_value(SessionUpdate::Notice(Notice::new(
2941 NoticeSeverity::Info,
2942 "Indexing workspace",
2943 )))
2944 .unwrap(),
2945 json!({
2946 "sessionUpdate": "notice",
2947 "severity": "info",
2948 "title": "Indexing workspace"
2949 })
2950 );
2951
2952 let SessionUpdate::Notice(notice) = serde_json::from_value(json!({
2953 "sessionUpdate": "notice",
2954 "severity": "critical",
2955 "title": "Provider degraded",
2956 "description": null,
2957 "_meta": null
2958 }))
2959 .unwrap() else {
2960 panic!("expected notice");
2961 };
2962
2963 assert_eq!(
2964 notice.severity,
2965 NoticeSeverity::Other("critical".to_string())
2966 );
2967 assert_eq!(notice.description, None);
2968 assert_eq!(notice.meta, None);
2969 assert_eq!(
2970 serde_json::to_value(SessionUpdate::Notice(notice)).unwrap(),
2971 json!({
2972 "sessionUpdate": "notice",
2973 "severity": "critical",
2974 "title": "Provider degraded"
2975 })
2976 );
2977 }
2978
2979 #[cfg(feature = "unstable_session_notices")]
2980 #[test]
2981 fn notice_requires_non_null_severity_and_title() {
2982 use serde_json::json;
2983
2984 for malformed in [
2985 json!({
2986 "sessionUpdate": "notice",
2987 "severity": "warning"
2988 }),
2989 json!({
2990 "sessionUpdate": "notice",
2991 "severity": "warning",
2992 "title": null
2993 }),
2994 json!({
2995 "sessionUpdate": "notice",
2996 "title": "MCP server unavailable"
2997 }),
2998 json!({
2999 "sessionUpdate": "notice",
3000 "severity": null,
3001 "title": "MCP server unavailable"
3002 }),
3003 ] {
3004 assert!(serde_json::from_value::<SessionUpdate>(malformed).is_err());
3005 }
3006 }
3007
3008 #[cfg(not(feature = "unstable_session_notices"))]
3009 #[test]
3010 fn unsupported_notice_is_rejected_by_closed_update_union() {
3011 use serde_json::json;
3012
3013 assert!(
3014 serde_json::from_value::<SessionUpdate>(json!({
3015 "sessionUpdate": "notice",
3016 "severity": "warning",
3017 "title": "MCP server unavailable"
3018 }))
3019 .is_err()
3020 );
3021 }
3022
3023 #[cfg(feature = "unstable_session_compaction")]
3024 #[test]
3025 fn compaction_updates_preserve_patch_and_open_status_semantics() {
3026 use serde_json::json;
3027
3028 assert_eq!(
3029 serde_json::to_value(SessionUpdate::CompactionUpdate(CompactionUpdate::new(
3030 "cmp_001",
3031 CompactionStatus::InProgress,
3032 )))
3033 .unwrap(),
3034 json!({
3035 "sessionUpdate": "compaction_update",
3036 "compactionId": "cmp_001",
3037 "status": "in_progress"
3038 })
3039 );
3040
3041 let SessionUpdate::CompactionUpdate(update) = serde_json::from_value(json!({
3042 "sessionUpdate": "compaction_update",
3043 "compactionId": "cmp_001",
3044 "status": "paused",
3045 "summary": null,
3046 "error": "waiting"
3047 }))
3048 .unwrap() else {
3049 panic!("expected compaction update");
3050 };
3051 assert_eq!(update.status, CompactionStatus::Other("paused".into()));
3052 assert!(update.summary.is_null());
3053 assert_eq!(update.error.value().map(String::as_str), Some("waiting"));
3054 assert!(update.meta.is_undefined());
3055 }
3056
3057 #[cfg(feature = "unstable_session_compaction")]
3058 #[test]
3059 fn compaction_chunk_and_v1_capability_serialize() {
3060 use serde_json::json;
3061
3062 assert_eq!(
3063 serde_json::to_value(SessionUpdate::CompactionSummaryChunk(
3064 CompactionSummaryChunk::new(
3065 "cmp_001",
3066 ContentBlock::Text(crate::v1::TextContent::new("retained")),
3067 ),
3068 ))
3069 .unwrap(),
3070 json!({
3071 "sessionUpdate": "compaction_summary_chunk",
3072 "compactionId": "cmp_001",
3073 "content": { "type": "text", "text": "retained" }
3074 })
3075 );
3076 assert_eq!(
3077 serde_json::to_value(
3078 ClientSessionCapabilities::new().compaction(CompactionCapabilities::new())
3079 )
3080 .unwrap(),
3081 json!({ "compaction": {} })
3082 );
3083 let absent: ClientSessionCapabilities = serde_json::from_value(json!({})).unwrap();
3084 let null: ClientSessionCapabilities =
3085 serde_json::from_value(json!({ "compaction": null })).unwrap();
3086 assert!(absent.compaction.is_none());
3087 assert!(null.compaction.is_none());
3088 }
3089
3090 #[test]
3091 fn test_elicitation_capability_semantics() {
3092 use serde_json::json;
3093
3094 let unsupported: ClientCapabilities = serde_json::from_value(json!({})).unwrap();
3095 assert!(unsupported.elicitation.is_none());
3096
3097 let null: ClientCapabilities =
3098 serde_json::from_value(json!({ "elicitation": null })).unwrap();
3099 assert!(null.elicitation.is_none());
3100
3101 let malformed: ClientCapabilities =
3102 serde_json::from_value(json!({ "elicitation": false })).unwrap();
3103 assert!(malformed.elicitation.is_none());
3104
3105 let empty: ClientCapabilities =
3106 serde_json::from_value(json!({ "elicitation": {} })).unwrap();
3107 let empty = empty.elicitation.expect("present capability");
3108 assert!(!empty.supports_form());
3109 assert!(!empty.supports_url());
3110
3111 let form_only: ClientCapabilities = serde_json::from_value(json!({
3112 "elicitation": { "form": {} }
3113 }))
3114 .unwrap();
3115 let form_only = form_only.elicitation.expect("advertised capability");
3116 assert!(form_only.supports_form());
3117 assert!(!form_only.supports_url());
3118
3119 let url_only: ClientCapabilities = serde_json::from_value(json!({
3120 "elicitation": { "url": {} }
3121 }))
3122 .unwrap();
3123 let url_only = url_only.elicitation.expect("advertised capability");
3124 assert!(!url_only.supports_form());
3125 assert!(url_only.supports_url());
3126
3127 let both: ClientCapabilities = serde_json::from_value(json!({
3128 "elicitation": { "form": {}, "url": {} }
3129 }))
3130 .unwrap();
3131 let both = both.elicitation.expect("advertised capability");
3132 assert!(both.supports_form());
3133 assert!(both.supports_url());
3134 }
3135
3136 #[test]
3137 fn test_elicitation_method_routing_and_envelopes() {
3138 use serde_json::json;
3139
3140 assert_eq!(CLIENT_METHOD_NAMES.elicitation_create, "elicitation/create");
3141 assert_eq!(
3142 CLIENT_METHOD_NAMES.elicitation_complete,
3143 "elicitation/complete"
3144 );
3145
3146 let request = AgentRequest::CreateElicitationRequest(CreateElicitationRequest::new(
3147 crate::v1::ElicitationFormMode::new(
3148 crate::v1::ElicitationSessionScope::new("sess_1"),
3149 crate::v1::ElicitationSchema::new(),
3150 ),
3151 "Choose a value",
3152 ));
3153 assert_eq!(request.method(), "elicitation/create");
3154 let method = Arc::from(request.method());
3155 let request = crate::v1::JsonRpcMessage::wrap(crate::v1::Request {
3156 id: crate::v1::RequestId::Number(7),
3157 method,
3158 params: Some(request),
3159 });
3160 assert_eq!(
3161 serde_json::to_value(request).unwrap(),
3162 json!({
3163 "jsonrpc": "2.0",
3164 "id": 7,
3165 "method": "elicitation/create",
3166 "params": {
3167 "mode": "form",
3168 "sessionId": "sess_1",
3169 "message": "Choose a value",
3170 "requestedSchema": { "type": "object", "properties": {} }
3171 }
3172 })
3173 );
3174
3175 let notification = AgentNotification::CompleteElicitationNotification(
3176 CompleteElicitationNotification::new("elic_1"),
3177 );
3178 assert_eq!(notification.method(), "elicitation/complete");
3179 let method = Arc::from(notification.method());
3180 let notification = crate::v1::JsonRpcMessage::wrap(crate::v1::Notification {
3181 method,
3182 params: Some(notification),
3183 });
3184 assert_eq!(
3185 serde_json::to_value(notification).unwrap(),
3186 json!({
3187 "jsonrpc": "2.0",
3188 "method": "elicitation/complete",
3189 "params": { "elicitationId": "elic_1" }
3190 })
3191 );
3192 }
3193
3194 #[test]
3195 fn test_client_capabilities_default_on_malformed_values() {
3196 use serde_json::json;
3197
3198 let capabilities: ClientCapabilities = serde_json::from_value(json!({
3199 "fs": {
3200 "readTextFile": "yes",
3201 "writeTextFile": true
3202 },
3203 "terminal": {}
3204 }))
3205 .unwrap();
3206
3207 assert!(!capabilities.fs.read_text_file);
3208 assert!(capabilities.fs.write_text_file);
3209 assert!(!capabilities.terminal);
3210
3211 let capabilities: ClientCapabilities = serde_json::from_value(json!({
3212 "fs": false
3213 }))
3214 .unwrap();
3215 assert_eq!(capabilities.fs, FileSystemCapabilities::default());
3216
3217 {
3218 let capabilities: ClientCapabilities = serde_json::from_value(json!({
3219 "auth": false
3220 }))
3221 .unwrap();
3222 assert_eq!(capabilities.auth, AuthCapabilities::default());
3223
3224 let capabilities: AuthCapabilities = serde_json::from_value(json!({
3225 "terminal": {}
3226 }))
3227 .unwrap();
3228 assert!(!capabilities.terminal);
3229 }
3230 }
3231
3232 #[test]
3233 fn test_serialization_behavior() {
3234 use serde_json::json;
3235
3236 assert_eq!(
3237 serde_json::from_value::<SessionInfoUpdate>(json!({})).unwrap(),
3238 SessionInfoUpdate {
3239 title: MaybeUndefined::Undefined,
3240 updated_at: MaybeUndefined::Undefined,
3241 meta: None
3242 }
3243 );
3244 assert_eq!(
3245 serde_json::from_value::<SessionInfoUpdate>(json!({"title": null, "updatedAt": null}))
3246 .unwrap(),
3247 SessionInfoUpdate {
3248 title: MaybeUndefined::Null,
3249 updated_at: MaybeUndefined::Null,
3250 meta: None
3251 }
3252 );
3253 assert_eq!(
3254 serde_json::from_value::<SessionInfoUpdate>(
3255 json!({"title": "title", "updatedAt": "timestamp"})
3256 )
3257 .unwrap(),
3258 SessionInfoUpdate {
3259 title: MaybeUndefined::Value("title".to_string()),
3260 updated_at: MaybeUndefined::Value("timestamp".to_string()),
3261 meta: None
3262 }
3263 );
3264
3265 assert_eq!(
3266 serde_json::to_value(SessionInfoUpdate::new()).unwrap(),
3267 json!({})
3268 );
3269 assert_eq!(
3270 serde_json::to_value(SessionInfoUpdate::new().title("title")).unwrap(),
3271 json!({"title": "title"})
3272 );
3273 assert_eq!(
3274 serde_json::to_value(SessionInfoUpdate::new().title(None)).unwrap(),
3275 json!({"title": null})
3276 );
3277 assert_eq!(
3278 serde_json::to_value(
3279 SessionInfoUpdate::new()
3280 .title("title")
3281 .title(MaybeUndefined::Undefined)
3282 )
3283 .unwrap(),
3284 json!({})
3285 );
3286 }
3287
3288 #[test]
3289 fn test_content_chunk_message_id_serialization() {
3290 use serde_json::json;
3291
3292 assert_eq!(
3293 serde_json::to_value(SessionUpdate::AgentMessageChunk(ContentChunk::new(
3294 ContentBlock::Text(crate::v1::TextContent::new("Hello"))
3295 )))
3296 .unwrap(),
3297 json!({
3298 "sessionUpdate": "agent_message_chunk",
3299 "content": {
3300 "type": "text",
3301 "text": "Hello"
3302 }
3303 })
3304 );
3305
3306 assert_eq!(
3307 serde_json::to_value(SessionUpdate::AgentMessageChunk(
3308 ContentChunk::new(ContentBlock::Text(crate::v1::TextContent::new("Hello")))
3309 .message_id("msg_agent_c42b9")
3310 ))
3311 .unwrap(),
3312 json!({
3313 "sessionUpdate": "agent_message_chunk",
3314 "messageId": "msg_agent_c42b9",
3315 "content": {
3316 "type": "text",
3317 "text": "Hello"
3318 }
3319 })
3320 );
3321
3322 let SessionUpdate::AgentMessageChunk(chunk) = serde_json::from_value(json!({
3323 "sessionUpdate": "agent_message_chunk",
3324 "messageId": null,
3325 "content": {
3326 "type": "text",
3327 "text": "Hello"
3328 }
3329 }))
3330 .unwrap() else {
3331 panic!("expected agent message chunk");
3332 };
3333
3334 assert_eq!(chunk.message_id, None);
3335 }
3336
3337 #[test]
3338 fn test_usage_update_serialization() {
3339 use serde_json::json;
3340
3341 assert_eq!(
3342 serde_json::to_value(SessionUpdate::UsageUpdate(UsageUpdate::new(
3343 53_000, 200_000
3344 )))
3345 .unwrap(),
3346 json!({
3347 "sessionUpdate": "usage_update",
3348 "used": 53000,
3349 "size": 200_000
3350 })
3351 );
3352
3353 assert_eq!(
3354 serde_json::to_value(SessionUpdate::UsageUpdate(
3355 UsageUpdate::new(53_000, 200_000).cost(Cost::new(0.045, "USD"))
3356 ))
3357 .unwrap(),
3358 json!({
3359 "sessionUpdate": "usage_update",
3360 "used": 53000,
3361 "size": 200_000,
3362 "cost": {
3363 "amount": 0.045,
3364 "currency": "USD"
3365 }
3366 })
3367 );
3368
3369 let SessionUpdate::UsageUpdate(update) = serde_json::from_value(json!({
3370 "sessionUpdate": "usage_update",
3371 "used": 53000,
3372 "size": 200_000,
3373 "cost": null
3374 }))
3375 .unwrap() else {
3376 panic!("expected usage update");
3377 };
3378
3379 assert_eq!(update.cost, None);
3380 }
3381
3382 #[cfg(feature = "unstable_nes")]
3383 #[test]
3384 fn test_client_capabilities_position_encodings_serialization() {
3385 use serde_json::json;
3386
3387 let capabilities = ClientCapabilities::new().position_encodings(vec![
3388 PositionEncodingKind::Utf32,
3389 PositionEncodingKind::Utf16,
3390 ]);
3391 let json = serde_json::to_value(&capabilities).unwrap();
3392
3393 assert_eq!(json["positionEncodings"], json!(["utf-32", "utf-16"]));
3394 }
3395
3396 #[test]
3397 fn test_client_capabilities_boolean_config_options_serialization() {
3398 use serde_json::json;
3399
3400 let capabilities = ClientCapabilities::new().session(
3401 ClientSessionCapabilities::new().config_options(
3402 SessionConfigOptionsCapabilities::new()
3403 .boolean(BooleanConfigOptionCapabilities::new()),
3404 ),
3405 );
3406 let json = serde_json::to_value(&capabilities).unwrap();
3407
3408 assert_eq!(json["session"]["configOptions"]["boolean"], json!({}));
3409
3410 let omitted: ClientCapabilities = serde_json::from_value(json!({})).unwrap();
3411 assert!(omitted.session.is_none());
3412
3413 let null_session: ClientCapabilities = serde_json::from_value(json!({
3414 "session": null
3415 }))
3416 .unwrap();
3417 assert!(null_session.session.is_none());
3418
3419 let null_config_options: ClientCapabilities = serde_json::from_value(json!({
3420 "session": {
3421 "configOptions": null
3422 }
3423 }))
3424 .unwrap();
3425 assert!(
3426 null_config_options
3427 .session
3428 .and_then(|session| session.config_options)
3429 .is_none()
3430 );
3431
3432 let null_boolean: ClientCapabilities = serde_json::from_value(json!({
3433 "session": {
3434 "configOptions": {
3435 "boolean": null
3436 }
3437 }
3438 }))
3439 .unwrap();
3440 assert!(
3441 null_boolean
3442 .session
3443 .and_then(|session| session.config_options)
3444 .and_then(|config_options| config_options.boolean)
3445 .is_none()
3446 );
3447 }
3448
3449 #[cfg(feature = "unstable_plan_operations")]
3450 #[test]
3451 fn test_plan_operations_serialization() {
3452 use serde_json::json;
3453
3454 use crate::v1::{PlanEntry, PlanEntryPriority, PlanEntryStatus, PlanUpdateContent};
3455
3456 let plan_update = SessionUpdate::PlanUpdate(PlanUpdate::new(PlanUpdateContent::items(
3457 "plan-1",
3458 vec![PlanEntry::new(
3459 "Step 1",
3460 PlanEntryPriority::High,
3461 PlanEntryStatus::Pending,
3462 )],
3463 )));
3464
3465 assert_eq!(
3466 serde_json::to_value(plan_update).unwrap(),
3467 json!({
3468 "sessionUpdate": "plan_update",
3469 "plan": {
3470 "type": "items",
3471 "planId": "plan-1",
3472 "entries": [
3473 {
3474 "content": "Step 1",
3475 "priority": "high",
3476 "status": "pending"
3477 }
3478 ]
3479 }
3480 })
3481 );
3482
3483 assert_eq!(
3484 serde_json::to_value(SessionUpdate::PlanRemoved(PlanRemoved::new("plan-1"))).unwrap(),
3485 json!({
3486 "sessionUpdate": "plan_removed",
3487 "planId": "plan-1"
3488 })
3489 );
3490
3491 let capabilities = ClientCapabilities::new().plan(PlanCapabilities::new());
3492 let json = serde_json::to_value(&capabilities).unwrap();
3493 assert_eq!(json["plan"], json!({}));
3494
3495 assert_eq!(
3496 serde_json::from_value::<ClientCapabilities>(json!({ "plan": null }))
3497 .unwrap()
3498 .plan,
3499 None
3500 );
3501 }
3502
3503 #[cfg(feature = "unstable_mcp_over_acp")]
3504 #[test]
3505 fn test_agent_mcp_request_method_names() {
3506 use serde_json::json;
3507
3508 let params: serde_json::Map<String, serde_json::Value> =
3509 [("cursor".to_string(), json!("abc"))].into_iter().collect();
3510
3511 assert_eq!(CLIENT_METHOD_NAMES.mcp_connect, "mcp/connect");
3512 assert_eq!(CLIENT_METHOD_NAMES.mcp_message, "mcp/message");
3513 assert_eq!(CLIENT_METHOD_NAMES.mcp_disconnect, "mcp/disconnect");
3514
3515 assert_eq!(
3516 AgentRequest::ConnectMcpRequest(ConnectMcpRequest::new("server-1")).method(),
3517 "mcp/connect"
3518 );
3519 assert_eq!(
3520 AgentRequest::MessageMcpRequest(MessageMcpRequest::new("conn-1", "tools/list"))
3521 .method(),
3522 "mcp/message"
3523 );
3524 assert_eq!(
3525 AgentRequest::DisconnectMcpRequest(DisconnectMcpRequest::new("conn-1")).method(),
3526 "mcp/disconnect"
3527 );
3528 assert_eq!(
3529 AgentNotification::MessageMcpNotification(MessageMcpNotification::new(
3530 "conn-1",
3531 "notifications/progress"
3532 ))
3533 .method(),
3534 "mcp/message"
3535 );
3536
3537 assert_eq!(
3538 serde_json::to_value(ConnectMcpRequest::new("server-1")).unwrap(),
3539 json!({ "serverId": "server-1" })
3540 );
3541 assert_eq!(
3542 serde_json::to_value(ConnectMcpResponse::new("conn-1")).unwrap(),
3543 json!({ "connectionId": "conn-1" })
3544 );
3545 assert_eq!(
3546 serde_json::to_value(MessageMcpRequest::new("conn-1", "tools/list").params(params))
3547 .unwrap(),
3548 json!({
3549 "connectionId": "conn-1",
3550 "method": "tools/list",
3551 "params": { "cursor": "abc" }
3552 })
3553 );
3554 assert_eq!(
3555 serde_json::to_value(DisconnectMcpRequest::new("conn-1")).unwrap(),
3556 json!({ "connectionId": "conn-1" })
3557 );
3558 assert_eq!(
3559 serde_json::to_value(MessageMcpNotification::new(
3560 "conn-1",
3561 "notifications/progress"
3562 ))
3563 .unwrap(),
3564 json!({
3565 "connectionId": "conn-1",
3566 "method": "notifications/progress"
3567 })
3568 );
3569
3570 let request_with_null_params: MessageMcpRequest = serde_json::from_value(json!({
3571 "connectionId": "conn-1",
3572 "method": "tools/list",
3573 "params": null
3574 }))
3575 .unwrap();
3576 assert_eq!(request_with_null_params.params, None);
3577 }
3578
3579 #[test]
3580 fn request_permission_request_rejects_malformed_options() {
3581 use serde_json::json;
3582
3583 assert!(
3584 serde_json::from_value::<RequestPermissionRequest>(json!({
3585 "sessionId": "sess-1",
3586 "toolCall": {"toolCallId": "tc-1"},
3587 "options": "not-an-array"
3588 }))
3589 .is_err()
3590 );
3591 assert!(
3592 serde_json::from_value::<RequestPermissionRequest>(json!({
3593 "sessionId": "sess-1",
3594 "toolCall": {"toolCallId": "tc-1"},
3595 "options": [{"optionId": "allow"}]
3596 }))
3597 .is_err()
3598 );
3599 }
3600}