agent_client_protocol/
client.rs

1//! Methods and notifications the client handles/receives
2
3use std::{fmt, path::PathBuf, sync::Arc};
4
5use anyhow::Result;
6use schemars::JsonSchema;
7use serde::{Deserialize, Serialize};
8
9use crate::{ContentBlock, Error, Plan, SessionId, ToolCall, ToolCallUpdate};
10
11pub trait Client {
12    fn request_permission(
13        &self,
14        args: RequestPermissionRequest,
15    ) -> impl Future<Output = Result<RequestPermissionResponse, Error>>;
16
17    fn write_text_file(
18        &self,
19        args: WriteTextFileRequest,
20    ) -> impl Future<Output = Result<(), Error>>;
21
22    fn read_text_file(
23        &self,
24        args: ReadTextFileRequest,
25    ) -> impl Future<Output = Result<ReadTextFileResponse, Error>>;
26
27    fn session_notification(
28        &self,
29        args: SessionNotification,
30    ) -> impl Future<Output = Result<(), Error>>;
31}
32
33// Session updates
34
35#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
36#[serde(rename_all = "camelCase")]
37pub struct SessionNotification {
38    pub session_id: SessionId,
39    #[serde(flatten)]
40    pub update: SessionUpdate,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
44#[serde(tag = "sessionUpdate", rename_all = "camelCase")]
45pub enum SessionUpdate {
46    UserMessageChunk { content: ContentBlock },
47    AgentMessageChunk { content: ContentBlock },
48    AgentThoughtChunk { content: ContentBlock },
49    ToolCall(ToolCall),
50    ToolCallUpdate(ToolCallUpdate),
51    Plan(Plan),
52}
53
54// Permission
55
56#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
57#[serde(rename_all = "camelCase")]
58pub struct RequestPermissionRequest {
59    pub session_id: SessionId,
60    pub tool_call: ToolCall,
61    pub options: Vec<PermissionOption>,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
65pub struct PermissionOption {
66    #[serde(rename = "optionId")]
67    pub id: PermissionOptionId,
68    pub label: String,
69    pub kind: PermissionOptionKind,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash)]
73#[serde(transparent)]
74pub struct PermissionOptionId(pub Arc<str>);
75
76impl fmt::Display for PermissionOptionId {
77    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78        self.0.fmt(f)
79    }
80}
81
82#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
83#[serde(rename_all = "camelCase")]
84pub enum PermissionOptionKind {
85    AllowOnce,
86    AllowAlways,
87    RejectOnce,
88    RejectAlways,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
92#[serde(rename_all = "camelCase")]
93pub struct RequestPermissionResponse {
94    // This extra-level is unfortunately needed because the output must be an object
95    pub outcome: RequestPermissionOutcome,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
99#[serde(tag = "outcome", rename_all = "camelCase")]
100pub enum RequestPermissionOutcome {
101    Cancelled,
102    #[serde(rename_all = "camelCase")]
103    Selected {
104        option_id: PermissionOptionId,
105    },
106}
107
108// Write text file
109
110#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
111#[serde(rename_all = "camelCase")]
112pub struct WriteTextFileRequest {
113    pub session_id: SessionId,
114    pub path: PathBuf,
115    pub content: String,
116}
117
118// Read text file
119
120#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
121#[serde(rename_all = "camelCase")]
122pub struct ReadTextFileRequest {
123    pub session_id: SessionId,
124    pub path: PathBuf,
125    #[serde(default, skip_serializing_if = "Option::is_none")]
126    pub line: Option<u32>,
127    #[serde(default, skip_serializing_if = "Option::is_none")]
128    pub limit: Option<u32>,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
132#[serde(rename_all = "camelCase")]
133pub struct ReadTextFileResponse {
134    pub content: String,
135}
136
137// Capabilities
138
139/// Capabilities supported by the client
140#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
141#[serde(rename_all = "camelCase")]
142pub struct ClientCapabilities {
143    /// FileSystem capabilities supported by the client.
144    #[serde(default)]
145    pub fs: FileSystemCapability,
146}
147
148#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
149#[serde(rename_all = "camelCase")]
150pub struct FileSystemCapability {
151    /// Client supports `fs/read_text_file`
152    #[serde(default)]
153    pub read_text_file: bool,
154    /// Client supports `fs/write_text_file`
155    #[serde(default)]
156    pub write_text_file: bool,
157}
158
159// Method schema
160
161#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct ClientMethodNames {
163    pub session_request_permission: &'static str,
164    pub session_update: &'static str,
165    pub fs_write_text_file: &'static str,
166    pub fs_read_text_file: &'static str,
167}
168
169pub const CLIENT_METHOD_NAMES: ClientMethodNames = ClientMethodNames {
170    session_update: SESSION_UPDATE_NOTIFICATION,
171    session_request_permission: SESSION_REQUEST_PERMISSION_METHOD_NAME,
172    fs_write_text_file: FS_WRITE_TEXT_FILE_METHOD_NAME,
173    fs_read_text_file: FS_READ_TEXT_FILE_METHOD_NAME,
174};
175
176pub const SESSION_UPDATE_NOTIFICATION: &str = "session/update";
177pub const SESSION_REQUEST_PERMISSION_METHOD_NAME: &str = "session/request_permission";
178pub const FS_WRITE_TEXT_FILE_METHOD_NAME: &str = "fs/write_text_file";
179pub const FS_READ_TEXT_FILE_METHOD_NAME: &str = "fs/read_text_file";
180
181/// Requests the agent sends to the client
182#[derive(Debug, Serialize, Deserialize, JsonSchema)]
183#[serde(untagged)]
184pub enum AgentRequest {
185    WriteTextFileRequest(WriteTextFileRequest),
186    ReadTextFileRequest(ReadTextFileRequest),
187    RequestPermissionRequest(RequestPermissionRequest),
188}
189
190/// Responses the client sends to the agent
191#[derive(Debug, Serialize, Deserialize, JsonSchema)]
192#[serde(untagged)]
193pub enum ClientResponse {
194    WriteTextFileResponse,
195    ReadTextFileResponse(ReadTextFileResponse),
196    RequestPermissionResponse(RequestPermissionResponse),
197}
198
199/// Notifications the agent sends to the client
200#[derive(Debug, Serialize, Deserialize, JsonSchema)]
201#[serde(untagged)]
202pub enum AgentNotification {
203    SessionNotification(SessionNotification),
204}