1use 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#[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#[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 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#[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#[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#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
141#[serde(rename_all = "camelCase")]
142pub struct ClientCapabilities {
143 #[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 #[serde(default)]
153 pub read_text_file: bool,
154 #[serde(default)]
156 pub write_text_file: bool,
157}
158
159#[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#[derive(Debug, Serialize, Deserialize, JsonSchema)]
183#[serde(untagged)]
184pub enum AgentRequest {
185 WriteTextFileRequest(WriteTextFileRequest),
186 ReadTextFileRequest(ReadTextFileRequest),
187 RequestPermissionRequest(RequestPermissionRequest),
188}
189
190#[derive(Debug, Serialize, Deserialize, JsonSchema)]
192#[serde(untagged)]
193pub enum ClientResponse {
194 WriteTextFileResponse,
195 ReadTextFileResponse(ReadTextFileResponse),
196 RequestPermissionResponse(RequestPermissionResponse),
197}
198
199#[derive(Debug, Serialize, Deserialize, JsonSchema)]
201#[serde(untagged)]
202pub enum AgentNotification {
203 SessionNotification(SessionNotification),
204}