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: ToolCallUpdate,
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, PartialEq, Eq)]
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 #[serde(alias = "cancelled")]
101 Canceled,
102 #[serde(rename_all = "camelCase")]
103 Selected { option_id: PermissionOptionId },
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
109#[serde(rename_all = "camelCase")]
110pub struct WriteTextFileRequest {
111 pub session_id: SessionId,
112 pub path: PathBuf,
113 pub content: String,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
119#[serde(rename_all = "camelCase")]
120pub struct ReadTextFileRequest {
121 pub session_id: SessionId,
122 pub path: PathBuf,
123 #[serde(default, skip_serializing_if = "Option::is_none")]
124 pub line: Option<u32>,
125 #[serde(default, skip_serializing_if = "Option::is_none")]
126 pub limit: Option<u32>,
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
130#[serde(rename_all = "camelCase")]
131pub struct ReadTextFileResponse {
132 pub content: String,
133}
134
135#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
139#[serde(rename_all = "camelCase")]
140pub struct ClientCapabilities {
141 #[serde(default)]
143 pub fs: FileSystemCapability,
144}
145
146#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
147#[serde(rename_all = "camelCase")]
148pub struct FileSystemCapability {
149 #[serde(default)]
151 pub read_text_file: bool,
152 #[serde(default)]
154 pub write_text_file: bool,
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct ClientMethodNames {
161 pub session_request_permission: &'static str,
162 pub session_update: &'static str,
163 pub fs_write_text_file: &'static str,
164 pub fs_read_text_file: &'static str,
165}
166
167pub const CLIENT_METHOD_NAMES: ClientMethodNames = ClientMethodNames {
168 session_update: SESSION_UPDATE_NOTIFICATION,
169 session_request_permission: SESSION_REQUEST_PERMISSION_METHOD_NAME,
170 fs_write_text_file: FS_WRITE_TEXT_FILE_METHOD_NAME,
171 fs_read_text_file: FS_READ_TEXT_FILE_METHOD_NAME,
172};
173
174pub const SESSION_UPDATE_NOTIFICATION: &str = "session/update";
175pub const SESSION_REQUEST_PERMISSION_METHOD_NAME: &str = "session/request_permission";
176pub const FS_WRITE_TEXT_FILE_METHOD_NAME: &str = "fs/write_text_file";
177pub const FS_READ_TEXT_FILE_METHOD_NAME: &str = "fs/read_text_file";
178
179#[derive(Debug, Serialize, Deserialize, JsonSchema)]
181#[serde(untagged)]
182pub enum AgentRequest {
183 WriteTextFileRequest(WriteTextFileRequest),
184 ReadTextFileRequest(ReadTextFileRequest),
185 RequestPermissionRequest(RequestPermissionRequest),
186}
187
188#[derive(Debug, Serialize, Deserialize, JsonSchema)]
190#[serde(untagged)]
191pub enum ClientResponse {
192 WriteTextFileResponse,
193 ReadTextFileResponse(ReadTextFileResponse),
194 RequestPermissionResponse(RequestPermissionResponse),
195}
196
197#[derive(Debug, Serialize, Deserialize, JsonSchema)]
199#[serde(untagged)]
200pub enum AgentNotification {
201 SessionNotification(SessionNotification),
202}