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