1use crate::error::Result;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::future::Future;
5use std::pin::Pin;
6use std::sync::Arc;
7
8pub mod messages;
9pub use messages::*;
10pub mod hooks;
11pub use hooks::*;
12pub mod config;
13pub use config::*;
14pub mod agent_options;
15pub use agent_options::*;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
20#[serde(rename_all = "camelCase")]
21pub enum PermissionMode {
22 Default,
23 AcceptEdits,
24 Plan,
25 BypassPermissions,
26 DontAsk,
27 Auto,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
31pub enum SdkBeta {
32 #[serde(rename = "context-1m-2025-08-07")]
33 Context1M20250807,
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
37#[serde(rename_all = "camelCase")]
38pub enum SettingSource {
39 User,
40 Project,
41 Local,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
45#[serde(rename_all = "camelCase")]
46pub enum ThinkingConfigType {
47 Adaptive,
48 Enabled,
49 Disabled,
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
58#[serde(rename_all = "lowercase")]
59pub enum EffortLevel {
60 Low,
61 Medium,
62 High,
63 Xhigh,
64 Max,
65}
66
67impl EffortLevel {
68 pub fn as_cli(&self) -> &'static str {
70 match self {
71 EffortLevel::Low => "low",
72 EffortLevel::Medium => "medium",
73 EffortLevel::High => "high",
74 EffortLevel::Xhigh => "xhigh",
75 EffortLevel::Max => "max",
76 }
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
81#[serde(rename_all = "camelCase")]
82pub enum UserContentKind {
83 Text,
84 Blocks,
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
88#[serde(rename_all = "snake_case")]
89pub enum AssistantMessageErrorKind {
90 AuthenticationFailed,
91 BillingError,
92 RateLimit,
93 InvalidRequest,
94 ServerError,
95 Unknown,
96}
97
98#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
99#[serde(rename_all = "snake_case")]
100pub enum TaskNotificationStatus {
101 Completed,
102 Failed,
103 Stopped,
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
112#[serde(rename_all = "snake_case")]
113pub enum TaskUpdatedStatus {
114 Pending,
115 Running,
116 Paused,
117 Completed,
118 Failed,
119 Killed,
120}
121
122pub const TERMINAL_TASK_STATUSES: [&str; 4] = ["completed", "failed", "stopped", "killed"];
128
129pub fn is_terminal_task_status(status: &str) -> bool {
132 TERMINAL_TASK_STATUSES.contains(&status)
133}
134
135#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
136#[serde(rename_all = "camelCase")]
137pub enum RateLimitStatus {
138 Allowed,
139 #[serde(alias = "allowed_warning")]
140 AllowedWarning,
141 Rejected,
142}
143
144#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
145#[serde(rename_all = "snake_case")]
146pub enum RateLimitType {
147 FiveHour,
148 SevenDay,
149 SevenDayOpus,
150 SevenDaySonnet,
151 Overage,
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
155#[serde(rename_all = "kebab-case")]
156pub enum MCPServerConnectionStatus {
157 Connected,
158 Failed,
159 #[serde(rename = "needs-auth")]
160 NeedsAuth,
161 Pending,
162 Disabled,
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize)]
168#[serde(tag = "type", rename_all = "camelCase")]
169pub enum MCPServerConfig {
170 #[serde(rename = "stdio")]
171 Stdio {
172 command: String,
173 #[serde(skip_serializing_if = "Option::is_none")]
174 args: Option<Vec<String>>,
175 #[serde(skip_serializing_if = "Option::is_none")]
176 env: Option<HashMap<String, String>>,
177 },
178 Sse {
179 url: String,
180 #[serde(skip_serializing_if = "Option::is_none")]
181 headers: Option<HashMap<String, String>>,
182 },
183 Http {
184 url: String,
185 #[serde(skip_serializing_if = "Option::is_none")]
186 headers: Option<HashMap<String, String>>,
187 },
188 #[serde(rename = "sdk")]
189 Sdk { name: String },
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize)]
195#[serde(rename_all = "camelCase")]
196pub struct ToolsPreset {
197 pub r#type: String,
198 pub preset: String,
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize)]
202#[serde(rename_all = "camelCase")]
203pub struct SystemPromptPreset {
204 pub r#type: String,
205 pub preset: String,
206 #[serde(skip_serializing_if = "Option::is_none")]
207 pub append: Option<String>,
208 #[serde(skip_serializing_if = "Option::is_none")]
209 pub exclude_dynamic_sections: Option<bool>,
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
213#[serde(rename_all = "camelCase")]
214pub struct SystemPromptFile {
215 pub r#type: String,
216 pub path: String,
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize)]
220#[serde(rename_all = "camelCase")]
221pub struct ThinkingConfig {
222 pub r#type: ThinkingConfigType,
223 #[serde(skip_serializing_if = "Option::is_none")]
224 pub budget_tokens: Option<i32>,
225 #[serde(skip_serializing_if = "Option::is_none")]
226 pub display: Option<String>,
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct TaskBudget {
231 pub total: i32,
232}
233
234#[derive(Debug, Clone, Serialize, Deserialize)]
235#[serde(rename_all = "camelCase")]
236pub struct PermissionRuleValue {
237 pub tool_name: String,
238 #[serde(skip_serializing_if = "Option::is_none")]
239 pub rule_content: Option<String>,
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize)]
243#[serde(rename_all = "camelCase")]
244pub struct PermissionUpdate {
245 pub r#type: String,
246 #[serde(skip_serializing_if = "Option::is_none")]
247 pub rules: Option<Vec<PermissionRuleValue>>,
248 #[serde(skip_serializing_if = "Option::is_none")]
249 pub behavior: Option<String>,
250 #[serde(skip_serializing_if = "Option::is_none")]
251 pub mode: Option<PermissionMode>,
252 #[serde(skip_serializing_if = "Option::is_none")]
253 pub directories: Option<Vec<String>>,
254 #[serde(skip_serializing_if = "Option::is_none")]
255 pub destination: Option<String>,
256}
257
258#[derive(Debug, Clone, Default)]
259pub struct ToolPermissionContext {
260 pub suggestions: Vec<PermissionUpdate>,
261 pub tool_use_id: Option<String>,
262 pub agent_id: Option<String>,
263 pub blocked_path: Option<String>,
264 pub decision_reason: Option<String>,
265 pub title: Option<String>,
266 pub display_name: Option<String>,
267 pub description: Option<String>,
268}
269
270#[derive(Debug, Clone)]
271pub enum PermissionResult {
272 Allow {
273 updated_input: Option<serde_json::Map<String, serde_json::Value>>,
274 updated_permissions: Option<Vec<PermissionUpdate>>,
275 },
276 Deny {
277 message: String,
278 interrupt: bool,
279 },
280}
281
282impl PermissionResult {
283 pub fn allow() -> Self {
284 Self::Allow {
285 updated_input: None,
286 updated_permissions: None,
287 }
288 }
289
290 pub fn deny(message: impl Into<String>) -> Self {
291 Self::Deny {
292 message: message.into(),
293 interrupt: false,
294 }
295 }
296}
297
298pub type CanUseToolFuture = Pin<Box<dyn Future<Output = Result<PermissionResult>> + Send>>;
299type CanUseToolFn = dyn Fn(
300 String,
301 serde_json::Map<String, serde_json::Value>,
302 ToolPermissionContext,
303 ) -> CanUseToolFuture
304 + Send
305 + Sync;
306
307#[derive(Clone)]
308pub struct CanUseToolCallback(Arc<CanUseToolFn>);
309
310impl std::fmt::Debug for CanUseToolCallback {
311 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
312 f.debug_tuple("CanUseToolCallback")
313 .field(&"<callback>")
314 .finish()
315 }
316}
317
318impl CanUseToolCallback {
319 pub fn new<F, Fut>(callback: F) -> Self
320 where
321 F: Fn(String, serde_json::Map<String, serde_json::Value>, ToolPermissionContext) -> Fut
322 + Send
323 + Sync
324 + 'static,
325 Fut: Future<Output = Result<PermissionResult>> + Send + 'static,
326 {
327 Self(Arc::new(move |tool_name, input, context| {
328 Box::pin(callback(tool_name, input, context))
329 }))
330 }
331
332 pub async fn call(
333 &self,
334 tool_name: String,
335 input: serde_json::Map<String, serde_json::Value>,
336 context: ToolPermissionContext,
337 ) -> Result<PermissionResult> {
338 (self.0)(tool_name, input, context).await
339 }
340}
341
342type StderrFn = dyn Fn(String) + Send + Sync;
343
344#[derive(Clone)]
345pub struct StderrCallback(Arc<StderrFn>);
346
347impl std::fmt::Debug for StderrCallback {
348 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
349 f.debug_tuple("StderrCallback")
350 .field(&"<callback>")
351 .finish()
352 }
353}
354
355impl StderrCallback {
356 pub fn new<F>(callback: F) -> Self
357 where
358 F: Fn(String) + Send + Sync + 'static,
359 {
360 Self(Arc::new(callback))
361 }
362
363 pub fn call(&self, line: String) {
364 (self.0)(line);
365 }
366}
367
368#[derive(Debug, Clone, Serialize, Deserialize)]
369#[serde(rename_all = "camelCase")]
370pub struct SDKPluginConfig {
371 pub r#type: String,
372 pub path: String,
373}
374
375#[derive(Debug, Clone, Serialize, Deserialize)]
376#[serde(rename_all = "camelCase")]
377pub struct MCPToolAnnotations {
378 #[serde(skip_serializing_if = "Option::is_none")]
379 pub read_only: Option<bool>,
380 #[serde(skip_serializing_if = "Option::is_none")]
381 pub destructive: Option<bool>,
382 #[serde(skip_serializing_if = "Option::is_none")]
383 pub open_world: Option<bool>,
384}
385
386#[derive(Debug, Clone, Serialize, Deserialize)]
387#[serde(rename_all = "camelCase")]
388pub struct MCPToolInfo {
389 pub name: String,
390 #[serde(skip_serializing_if = "Option::is_none")]
391 pub description: Option<String>,
392 #[serde(skip_serializing_if = "Option::is_none")]
393 pub annotations: Option<MCPToolAnnotations>,
394}
395
396#[derive(Debug, Clone, Serialize, Deserialize)]
397#[serde(rename_all = "camelCase")]
398pub struct MCPServerInfo {
399 pub name: String,
400 pub version: String,
401}
402
403#[derive(Debug, Clone, Serialize, Deserialize)]
404#[serde(rename_all = "camelCase")]
405pub struct MCPServerStatus {
406 pub name: String,
407 pub status: MCPServerConnectionStatus,
408 #[serde(skip_serializing_if = "Option::is_none")]
409 pub server_info: Option<MCPServerInfo>,
410 #[serde(skip_serializing_if = "Option::is_none")]
411 pub error: Option<String>,
412 #[serde(skip_serializing_if = "Option::is_none")]
413 pub config: Option<MCPServerStatusConfig>,
414 #[serde(skip_serializing_if = "Option::is_none")]
415 pub scope: Option<String>,
416 #[serde(skip_serializing_if = "Option::is_none")]
417 pub tools: Option<Vec<MCPToolInfo>>,
418}
419
420#[derive(Debug, Clone, Serialize, Deserialize)]
421#[serde(rename_all = "camelCase")]
422pub struct MCPStatusResponse {
423 pub mcp_servers: Vec<MCPServerStatus>,
424}
425
426#[derive(Debug, Clone, Serialize, Deserialize)]
427#[serde(rename_all = "camelCase")]
428pub struct ContextUsageCategory {
429 pub name: String,
430 pub tokens: i32,
431 pub color: String,
432 #[serde(skip_serializing_if = "Option::is_none")]
433 pub is_deferred: Option<bool>,
434}
435
436#[derive(Debug, Clone, Serialize, Deserialize)]
437#[serde(rename_all = "camelCase")]
438pub struct ContextUsageResponse {
439 pub categories: Vec<ContextUsageCategory>,
440 pub total_tokens: i32,
441 pub max_tokens: i32,
442 pub raw_max_tokens: i32,
443 pub percentage: f64,
444 pub model: String,
445 pub is_auto_compact_enabled: bool,
446 pub memory_files: Vec<serde_json::Map<String, serde_json::Value>>,
447 pub mcp_tools: Vec<serde_json::Map<String, serde_json::Value>>,
448 pub agents: Vec<serde_json::Map<String, serde_json::Value>>,
449 pub grid_rows: Vec<Vec<serde_json::Map<String, serde_json::Value>>>,
450 #[serde(skip_serializing_if = "Option::is_none")]
451 pub auto_compact_threshold: Option<i32>,
452 #[serde(skip_serializing_if = "Option::is_none")]
453 pub deferred_builtin_tools: Option<Vec<serde_json::Map<String, serde_json::Value>>>,
454 #[serde(skip_serializing_if = "Option::is_none")]
455 pub system_tools: Option<Vec<serde_json::Map<String, serde_json::Value>>>,
456 #[serde(skip_serializing_if = "Option::is_none")]
457 pub system_prompt_sections: Option<Vec<serde_json::Map<String, serde_json::Value>>>,
458 #[serde(skip_serializing_if = "Option::is_none")]
459 pub slash_commands: Option<serde_json::Value>,
460 #[serde(skip_serializing_if = "Option::is_none")]
461 pub skills: Option<serde_json::Value>,
462}
463
464#[derive(Debug, Clone, Serialize, Deserialize)]
465#[serde(rename_all = "camelCase")]
466pub struct TaskUsage {
467 #[serde(alias = "total_tokens")]
468 pub total_tokens: i32,
469 #[serde(alias = "tool_uses")]
470 pub tool_uses: i32,
471 #[serde(alias = "duration_ms")]
472 pub duration_ms: i32,
473}
474
475#[derive(Debug, Clone, Serialize, Deserialize)]
476#[serde(rename_all = "camelCase")]
477pub struct RateLimitInfo {
478 pub status: RateLimitStatus,
479 #[serde(skip_serializing_if = "Option::is_none")]
480 pub resets_at: Option<i64>,
481 #[serde(skip_serializing_if = "Option::is_none")]
482 pub rate_limit_type: Option<RateLimitType>,
483 #[serde(skip_serializing_if = "Option::is_none")]
484 pub utilization: Option<f64>,
485 #[serde(skip_serializing_if = "Option::is_none")]
486 pub overage_status: Option<RateLimitStatus>,
487 #[serde(skip_serializing_if = "Option::is_none")]
488 pub overage_resets_at: Option<i64>,
489 #[serde(skip_serializing_if = "Option::is_none")]
490 pub overage_disabled_reason: Option<String>,
491 #[serde(skip_serializing_if = "Option::is_none")]
492 pub raw: Option<serde_json::Map<String, serde_json::Value>>,
493}
494
495#[derive(Debug, Clone, Serialize, Deserialize)]
498#[serde(tag = "type", rename_all = "camelCase")]
499pub enum MCPServerStatusConfig {
500 #[serde(rename = "sdk")]
501 Sdk { name: String },
502 #[serde(rename = "claudeai-proxy")]
503 ClaudeAiProxy { url: String, id: String },
504 Sse {
505 url: String,
506 #[serde(skip_serializing_if = "Option::is_none")]
507 headers: Option<HashMap<String, String>>,
508 },
509 Http {
510 url: String,
511 #[serde(skip_serializing_if = "Option::is_none")]
512 headers: Option<HashMap<String, String>>,
513 },
514 #[serde(rename = "stdio")]
515 Stdio {
516 #[serde(skip_serializing_if = "Option::is_none")]
517 command: Option<String>,
518 #[serde(skip_serializing_if = "Option::is_none")]
519 args: Option<Vec<String>>,
520 #[serde(skip_serializing_if = "Option::is_none")]
521 env: Option<HashMap<String, String>>,
522 },
523}