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 pub update: SessionUpdate,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
43#[serde(tag = "sessionUpdate", rename_all = "snake_case")]
44pub enum SessionUpdate {
45 UserMessageChunk { content: ContentBlock },
46 AgentMessageChunk { content: ContentBlock },
47 AgentThoughtChunk { content: ContentBlock },
48 ToolCall(ToolCall),
49 ToolCallUpdate(ToolCallUpdate),
50 Plan(Plan),
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
56#[serde(rename_all = "camelCase")]
57pub struct RequestPermissionRequest {
58 pub session_id: SessionId,
59 pub tool_call: ToolCall,
60 pub options: Vec<PermissionOption>,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
64pub struct PermissionOption {
65 #[serde(rename = "optionId")]
66 pub id: PermissionOptionId,
67 pub name: String,
68 pub kind: PermissionOptionKind,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash)]
72#[serde(transparent)]
73pub struct PermissionOptionId(pub Arc<str>);
74
75impl fmt::Display for PermissionOptionId {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 self.0.fmt(f)
78 }
79}
80
81#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
82#[serde(rename_all = "snake_case")]
83pub enum PermissionOptionKind {
84 AllowOnce,
85 AllowAlways,
86 RejectOnce,
87 RejectAlways,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
91#[serde(rename_all = "camelCase")]
92pub struct RequestPermissionResponse {
93 pub outcome: RequestPermissionOutcome,
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
98#[serde(tag = "outcome", rename_all = "snake_case")]
99pub enum RequestPermissionOutcome {
100 Cancelled,
101 #[serde(rename_all = "camelCase")]
102 Selected {
103 option_id: PermissionOptionId,
104 },
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
110#[serde(rename_all = "camelCase")]
111pub struct WriteTextFileRequest {
112 pub session_id: SessionId,
113 pub path: PathBuf,
114 pub content: String,
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
120#[serde(rename_all = "camelCase")]
121pub struct ReadTextFileRequest {
122 pub session_id: SessionId,
123 pub path: PathBuf,
124 #[serde(default, skip_serializing_if = "Option::is_none")]
125 pub line: Option<u32>,
126 #[serde(default, skip_serializing_if = "Option::is_none")]
127 pub limit: Option<u32>,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
131#[serde(rename_all = "camelCase")]
132pub struct ReadTextFileResponse {
133 pub content: String,
134}
135
136#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
140#[serde(rename_all = "camelCase")]
141pub struct ClientCapabilities {
142 #[serde(default)]
144 pub fs: FileSystemCapability,
145}
146
147#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
148#[serde(rename_all = "camelCase")]
149pub struct FileSystemCapability {
150 #[serde(default)]
152 pub read_text_file: bool,
153 #[serde(default)]
155 pub write_text_file: bool,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct ClientMethodNames {
162 pub session_request_permission: &'static str,
163 pub session_update: &'static str,
164 pub fs_write_text_file: &'static str,
165 pub fs_read_text_file: &'static str,
166}
167
168pub const CLIENT_METHOD_NAMES: ClientMethodNames = ClientMethodNames {
169 session_update: SESSION_UPDATE_NOTIFICATION,
170 session_request_permission: SESSION_REQUEST_PERMISSION_METHOD_NAME,
171 fs_write_text_file: FS_WRITE_TEXT_FILE_METHOD_NAME,
172 fs_read_text_file: FS_READ_TEXT_FILE_METHOD_NAME,
173};
174
175pub const SESSION_UPDATE_NOTIFICATION: &str = "session/update";
176pub const SESSION_REQUEST_PERMISSION_METHOD_NAME: &str = "session/request_permission";
177pub const FS_WRITE_TEXT_FILE_METHOD_NAME: &str = "fs/write_text_file";
178pub const FS_READ_TEXT_FILE_METHOD_NAME: &str = "fs/read_text_file";
179
180#[derive(Debug, Serialize, Deserialize, JsonSchema)]
182#[serde(untagged)]
183pub enum AgentRequest {
184 WriteTextFileRequest(WriteTextFileRequest),
185 ReadTextFileRequest(ReadTextFileRequest),
186 RequestPermissionRequest(RequestPermissionRequest),
187}
188
189#[derive(Debug, Serialize, Deserialize, JsonSchema)]
191#[serde(untagged)]
192pub enum ClientResponse {
193 WriteTextFileResponse,
194 ReadTextFileResponse(ReadTextFileResponse),
195 RequestPermissionResponse(RequestPermissionResponse),
196}
197
198#[derive(Debug, Serialize, Deserialize, JsonSchema)]
200#[serde(untagged)]
201pub enum AgentNotification {
202 SessionNotification(SessionNotification),
203}