agent_client_protocol_schema/v2/
mod.rs1mod agent;
13mod client;
14mod content;
15mod elicitation;
16mod error;
17mod ext;
18#[cfg(feature = "unstable_mcp_over_acp")]
19mod mcp;
20mod plan;
21mod protocol_level;
22#[cfg(feature = "unstable_proxy_chains")]
23mod proxy;
24mod rpc;
25#[cfg(feature = "schemars")]
26pub(crate) mod schema_util;
27mod terminal;
28mod tool_call;
29
30pub use crate::rpc::{JsonRpcMessage, Notification};
31pub use agent::*;
32pub use client::*;
33pub use content::*;
34use derive_more::{Display, From};
35pub use elicitation::*;
36pub use error::*;
37pub use ext::*;
38#[cfg(feature = "unstable_mcp_over_acp")]
39pub use mcp::*;
40pub use plan::*;
41pub use protocol_level::*;
42#[cfg(feature = "unstable_proxy_chains")]
43pub use proxy::*;
44pub use rpc::*;
45pub use serde_json::value::RawValue;
46pub use terminal::*;
47pub use tool_call::*;
48
49use serde::{Deserialize, Serialize};
50use std::{
51 borrow::Cow,
52 ffi::{OsStr, OsString},
53 path::{Path, PathBuf},
54 sync::Arc,
55};
56
57#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
62#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From)]
63#[serde(transparent)]
64#[from(forward)]
65#[non_exhaustive]
66pub struct AbsolutePath(pub PathBuf);
67
68impl AbsolutePath {
69 #[must_use]
71 pub fn new(path: impl Into<Self>) -> Self {
72 path.into()
73 }
74
75 #[must_use]
77 pub fn into_inner(self) -> PathBuf {
78 self.0
79 }
80}
81
82impl AsRef<Path> for AbsolutePath {
83 fn as_ref(&self) -> &Path {
84 self.0.as_path()
85 }
86}
87
88impl AsRef<OsStr> for AbsolutePath {
89 fn as_ref(&self) -> &OsStr {
90 self.0.as_os_str()
91 }
92}
93
94macro_rules! impl_into_option_conversion {
95 ($target:ty, $source:ty) => {
96 impl crate::IntoOption<$target> for $source {
97 fn into_option(self) -> Option<$target> {
98 Some(self.into())
99 }
100 }
101 };
102}
103
104macro_rules! impl_into_maybe_undefined_conversion {
105 ($target:ty, $source:ty) => {
106 impl crate::IntoMaybeUndefined<$target> for $source {
107 fn into_maybe_undefined(self) -> crate::MaybeUndefined<$target> {
108 crate::MaybeUndefined::Value(self.into())
109 }
110 }
111 };
112}
113
114impl_into_option_conversion!(AbsolutePath, PathBuf);
115impl_into_option_conversion!(AbsolutePath, OsString);
116impl_into_option_conversion!(AbsolutePath, String);
117impl_into_option_conversion!(AbsolutePath, Box<Path>);
118impl_into_option_conversion!(AbsolutePath, Cow<'_, Path>);
119impl_into_maybe_undefined_conversion!(AbsolutePath, PathBuf);
120impl_into_maybe_undefined_conversion!(AbsolutePath, OsString);
121impl_into_maybe_undefined_conversion!(AbsolutePath, String);
122impl_into_maybe_undefined_conversion!(AbsolutePath, Box<Path>);
123impl_into_maybe_undefined_conversion!(AbsolutePath, Cow<'_, Path>);
124
125impl<T: ?Sized + AsRef<OsStr>> crate::IntoOption<AbsolutePath> for &T {
126 fn into_option(self) -> Option<AbsolutePath> {
127 Some(self.into())
128 }
129}
130
131impl<T: ?Sized + AsRef<OsStr>> crate::IntoMaybeUndefined<AbsolutePath> for &T {
132 fn into_maybe_undefined(self) -> crate::MaybeUndefined<AbsolutePath> {
133 crate::MaybeUndefined::Value(self.into())
134 }
135}
136#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
143#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
144#[serde(transparent)]
145#[from(forward)]
146#[non_exhaustive]
147pub struct SessionId(pub Arc<str>);
148
149impl SessionId {
150 #[must_use]
152 pub fn new(id: impl Into<Self>) -> Self {
153 id.into()
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn semantic_newtype_builders_remain_ergonomic() {
163 let request = NewSessionRequest::new("/workspace")
164 .additional_directories(["/workspace/shared", "/workspace/docs"]);
165 assert_eq!(request.cwd, AbsolutePath::new("/workspace"));
166 assert_eq!(
167 <AbsolutePath as AsRef<Path>>::as_ref(&request.cwd),
168 Path::new("/workspace")
169 );
170 assert_eq!(
171 <AbsolutePath as AsRef<OsStr>>::as_ref(&request.cwd),
172 OsStr::new("/workspace")
173 );
174
175 let list = ListSessionsRequest::new()
176 .cwd("/workspace")
177 .cursor("next-page");
178 assert_eq!(list.cursor, Some(SessionListCursor::new("next-page")));
179 assert_eq!(
180 <SessionListCursor as AsRef<str>>::as_ref(list.cursor.as_ref().unwrap()),
181 "next-page"
182 );
183
184 let image = ImageContent::new("aGVsbG8=", "image/png");
185 assert_eq!(image.mime_type, MediaType::new("image/png"));
186 assert_eq!(
187 <MediaType as AsRef<str>>::as_ref(&image.mime_type),
188 "image/png"
189 );
190
191 let session_id_source = String::from("session-1");
192 let session_id = SessionId::new(session_id_source.as_str());
193 assert_eq!(SessionId::new(session_id).to_string(), "session-1");
194
195 let os_path = OsString::from("/workspace");
196 assert_eq!(
197 ListSessionsRequest::new().cwd(&os_path).cwd,
198 Some(AbsolutePath::new(&os_path))
199 );
200 drop(TerminalUpdate::new("terminal-1").cwd(&os_path));
201
202 drop(ListSessionsRequest::new().cwd(None).cursor(None));
203 drop(ListSessionsResponse::new(Vec::new()).next_cursor(None));
204 drop(Icon::new("https://example.com/icon.png").mime_type(None));
205 drop(TerminalUpdate::new("terminal-1").cwd(None));
206 }
207}