1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::future::Future;
9use std::path::PathBuf;
10use std::pin::Pin;
11use std::sync::Arc;
12
13use crate::error::Result;
14
15#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
21#[serde(transparent)]
22pub struct SessionId(String);
23
24impl SessionId {
25 pub fn new(id: impl Into<String>) -> Self {
27 Self(id.into())
28 }
29
30 pub fn as_str(&self) -> &str {
32 &self.0
33 }
34}
35
36impl Default for SessionId {
37 fn default() -> Self {
38 Self("default".to_string())
39 }
40}
41
42impl From<String> for SessionId {
43 fn from(s: String) -> Self {
44 Self(s)
45 }
46}
47
48impl From<&str> for SessionId {
49 fn from(s: &str) -> Self {
50 Self(s.to_string())
51 }
52}
53
54#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
56#[serde(transparent)]
57pub struct ToolName(String);
58
59impl ToolName {
60 pub fn new(name: impl Into<String>) -> Self {
62 Self(name.into())
63 }
64
65 pub fn as_str(&self) -> &str {
67 &self.0
68 }
69}
70
71impl From<String> for ToolName {
72 fn from(s: String) -> Self {
73 Self(s)
74 }
75}
76
77impl From<&str> for ToolName {
78 fn from(s: &str) -> Self {
79 Self(s.to_string())
80 }
81}
82
83#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
85#[serde(transparent)]
86pub struct RequestId(String);
87
88impl RequestId {
89 pub fn new(id: impl Into<String>) -> Self {
91 Self(id.into())
92 }
93
94 pub fn as_str(&self) -> &str {
96 &self.0
97 }
98}
99
100impl From<String> for RequestId {
101 fn from(s: String) -> Self {
102 Self(s)
103 }
104}
105
106impl From<&str> for RequestId {
107 fn from(s: &str) -> Self {
108 Self(s.to_string())
109 }
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
118#[serde(rename_all = "camelCase")]
119pub enum PermissionMode {
120 Default,
122 AcceptEdits,
124 Plan,
126 BypassPermissions,
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
132#[serde(rename_all = "lowercase")]
133pub enum SettingSource {
134 User,
136 Project,
138 Local,
140}
141
142#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
144#[serde(rename_all = "camelCase")]
145pub enum PermissionUpdateDestination {
146 UserSettings,
148 ProjectSettings,
150 LocalSettings,
152 Session,
154}
155
156#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
158#[serde(rename_all = "lowercase")]
159pub enum PermissionBehavior {
160 Allow,
162 Deny,
164 Ask,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct PermissionRuleValue {
171 pub tool_name: String,
173 #[serde(skip_serializing_if = "Option::is_none")]
175 pub rule_content: Option<String>,
176}
177
178#[derive(Debug, Clone, Serialize, Deserialize)]
180#[serde(tag = "type", rename_all = "camelCase")]
181pub enum PermissionUpdate {
182 AddRules {
184 #[serde(skip_serializing_if = "Option::is_none")]
186 rules: Option<Vec<PermissionRuleValue>>,
187 #[serde(skip_serializing_if = "Option::is_none")]
189 destination: Option<PermissionUpdateDestination>,
190 },
191 ReplaceRules {
193 #[serde(skip_serializing_if = "Option::is_none")]
195 rules: Option<Vec<PermissionRuleValue>>,
196 #[serde(skip_serializing_if = "Option::is_none")]
198 destination: Option<PermissionUpdateDestination>,
199 },
200 RemoveRules {
202 #[serde(skip_serializing_if = "Option::is_none")]
204 rules: Option<Vec<PermissionRuleValue>>,
205 #[serde(skip_serializing_if = "Option::is_none")]
207 destination: Option<PermissionUpdateDestination>,
208 },
209 SetMode {
211 mode: PermissionMode,
213 #[serde(skip_serializing_if = "Option::is_none")]
215 destination: Option<PermissionUpdateDestination>,
216 },
217 AddDirectories {
219 #[serde(skip_serializing_if = "Option::is_none")]
221 directories: Option<Vec<String>>,
222 #[serde(skip_serializing_if = "Option::is_none")]
224 destination: Option<PermissionUpdateDestination>,
225 },
226 RemoveDirectories {
228 #[serde(skip_serializing_if = "Option::is_none")]
230 directories: Option<Vec<String>>,
231 #[serde(skip_serializing_if = "Option::is_none")]
233 destination: Option<PermissionUpdateDestination>,
234 },
235}
236
237#[derive(Debug, Clone, Serialize, Deserialize)]
239pub struct ToolPermissionContext {
240 pub suggestions: Vec<PermissionUpdate>,
242}
243
244#[derive(Debug, Clone, Serialize, Deserialize)]
246pub struct PermissionRequest {
247 pub tool_name: ToolName,
249 pub tool_input: serde_json::Value,
251 pub context: ToolPermissionContext,
253}
254
255#[derive(Debug, Clone, Serialize, Deserialize)]
257pub struct PermissionResultAllow {
258 #[serde(skip_serializing_if = "Option::is_none")]
260 pub updated_input: Option<serde_json::Value>,
261 #[serde(skip_serializing_if = "Option::is_none")]
263 pub updated_permissions: Option<Vec<PermissionUpdate>>,
264}
265
266#[derive(Debug, Clone, Serialize, Deserialize)]
268pub struct PermissionResultDeny {
269 pub message: String,
271 #[serde(default)]
273 pub interrupt: bool,
274}
275
276#[derive(Debug, Clone, Serialize, Deserialize)]
278#[serde(tag = "type", rename_all = "lowercase")]
279pub enum PermissionResult {
280 Allow(PermissionResultAllow),
282 Deny(PermissionResultDeny),
284}
285
286pub type CanUseToolCallback = Arc<
288 dyn Fn(
289 ToolName,
290 serde_json::Value,
291 ToolPermissionContext,
292 ) -> Pin<Box<dyn Future<Output = Result<PermissionResult>> + Send>>
293 + Send
294 + Sync,
295>;
296
297#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
303pub enum HookEvent {
304 PreToolUse,
306 PostToolUse,
308 UserPromptSubmit,
310 Stop,
312 SubagentStop,
314 PreCompact,
316}
317
318#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
320#[serde(rename_all = "lowercase")]
321pub enum HookDecision {
322 Block,
324}
325
326#[derive(Debug, Clone, Default, Serialize, Deserialize)]
328pub struct HookOutput {
329 #[serde(skip_serializing_if = "Option::is_none")]
331 pub decision: Option<HookDecision>,
332 #[serde(skip_serializing_if = "Option::is_none", rename = "systemMessage")]
334 pub system_message: Option<String>,
335 #[serde(skip_serializing_if = "Option::is_none", rename = "hookSpecificOutput")]
337 pub hook_specific_output: Option<serde_json::Value>,
338}
339
340#[derive(Debug, Clone)]
342pub struct HookContext {
343 }
345
346pub type HookCallback = Arc<
348 dyn Fn(
349 serde_json::Value,
350 Option<String>,
351 HookContext,
352 ) -> Pin<Box<dyn Future<Output = Result<HookOutput>> + Send>>
353 + Send
354 + Sync,
355>;
356
357#[derive(Clone)]
359pub struct HookMatcher {
360 pub matcher: Option<String>,
362 pub hooks: Vec<HookCallback>,
364}
365
366impl std::fmt::Debug for HookMatcher {
367 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
368 f.debug_struct("HookMatcher")
369 .field("matcher", &self.matcher)
370 .field("hooks", &format!("[{} callbacks]", self.hooks.len()))
371 .finish()
372 }
373}
374
375#[derive(Debug, Clone, Serialize, Deserialize)]
381pub struct McpStdioServerConfig {
382 #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
384 pub server_type: Option<String>,
385 pub command: String,
387 #[serde(skip_serializing_if = "Option::is_none")]
389 pub args: Option<Vec<String>>,
390 #[serde(skip_serializing_if = "Option::is_none")]
392 pub env: Option<HashMap<String, String>>,
393}
394
395#[derive(Debug, Clone, Serialize, Deserialize)]
397pub struct McpSseServerConfig {
398 #[serde(rename = "type")]
400 pub server_type: String,
401 pub url: String,
403 #[serde(skip_serializing_if = "Option::is_none")]
405 pub headers: Option<HashMap<String, String>>,
406}
407
408#[derive(Debug, Clone, Serialize, Deserialize)]
410pub struct McpHttpServerConfig {
411 #[serde(rename = "type")]
413 pub server_type: String,
414 pub url: String,
416 #[serde(skip_serializing_if = "Option::is_none")]
418 pub headers: Option<HashMap<String, String>>,
419}
420
421#[derive(Debug, Clone)]
423pub struct SdkMcpServerMarker {
424 #[allow(dead_code)]
426 pub name: String,
427}
428
429#[derive(Debug, Clone)]
431pub enum McpServerConfig {
432 Stdio(McpStdioServerConfig),
434 Sse(McpSseServerConfig),
436 Http(McpHttpServerConfig),
438 Sdk(SdkMcpServerMarker),
440}
441
442#[derive(Debug, Clone, Default)]
444pub enum McpServers {
445 #[default]
447 None,
448 Dict(HashMap<String, McpServerConfig>),
450 Path(PathBuf),
452}
453
454#[derive(Debug, Clone, Serialize, Deserialize)]
460#[serde(untagged)]
461pub enum ContentValue {
462 String(String),
464 Blocks(Vec<serde_json::Value>),
466}
467
468#[derive(Debug, Clone, Serialize, Deserialize)]
470#[serde(tag = "type", rename_all = "snake_case")]
471pub enum ContentBlock {
472 Text {
474 text: String,
476 },
477 Thinking {
479 thinking: String,
481 signature: String,
483 },
484 ToolUse {
486 id: String,
488 name: String,
490 input: serde_json::Value,
492 },
493 ToolResult {
495 tool_use_id: String,
497 #[serde(skip_serializing_if = "Option::is_none")]
499 content: Option<ContentValue>,
500 #[serde(skip_serializing_if = "Option::is_none")]
502 is_error: Option<bool>,
503 },
504}
505
506#[derive(Debug, Clone, Serialize, Deserialize)]
508pub struct UserMessageContent {
509 pub role: String,
511 #[serde(skip_serializing_if = "Option::is_none")]
513 pub content: Option<UserContent>,
514}
515
516#[derive(Debug, Clone, Serialize, Deserialize)]
518#[serde(untagged)]
519pub enum UserContent {
520 String(String),
522 Blocks(Vec<ContentBlock>),
524}
525
526#[derive(Debug, Clone, Serialize, Deserialize)]
528pub struct AssistantMessageContent {
529 pub model: String,
531 pub content: Vec<ContentBlock>,
533}
534
535#[derive(Debug, Clone, Serialize, Deserialize)]
537#[serde(tag = "type", rename_all = "snake_case")]
538pub enum Message {
539 User {
541 #[serde(skip_serializing_if = "Option::is_none")]
543 parent_tool_use_id: Option<String>,
544 message: UserMessageContent,
546 #[serde(skip_serializing_if = "Option::is_none")]
548 session_id: Option<SessionId>,
549 },
550 Assistant {
552 #[serde(skip_serializing_if = "Option::is_none")]
554 parent_tool_use_id: Option<String>,
555 message: AssistantMessageContent,
557 #[serde(skip_serializing_if = "Option::is_none")]
559 session_id: Option<SessionId>,
560 },
561 System {
563 subtype: String,
565 #[serde(flatten)]
567 data: serde_json::Value,
568 },
569 Result {
571 subtype: String,
573 duration_ms: u64,
575 duration_api_ms: u64,
577 is_error: bool,
579 num_turns: u32,
581 session_id: SessionId,
583 #[serde(skip_serializing_if = "Option::is_none")]
585 total_cost_usd: Option<f64>,
586 #[serde(skip_serializing_if = "Option::is_none")]
588 usage: Option<serde_json::Value>,
589 #[serde(skip_serializing_if = "Option::is_none")]
591 result: Option<String>,
592 },
593 StreamEvent {
595 uuid: String,
597 session_id: SessionId,
599 event: serde_json::Value,
601 #[serde(skip_serializing_if = "Option::is_none")]
603 parent_tool_use_id: Option<String>,
604 },
605}
606
607#[derive(Debug, Clone, Serialize, Deserialize)]
613pub struct SystemPromptPreset {
614 #[serde(rename = "type")]
616 pub prompt_type: String,
617 pub preset: String,
619 #[serde(skip_serializing_if = "Option::is_none")]
621 pub append: Option<String>,
622}
623
624#[derive(Debug, Clone)]
626pub enum SystemPrompt {
627 String(String),
629 Preset(SystemPromptPreset),
631}
632
633#[derive(Debug, Clone, Serialize, Deserialize)]
639pub struct AgentDefinition {
640 pub description: String,
642 pub prompt: String,
644 #[serde(skip_serializing_if = "Option::is_none")]
646 pub tools: Option<Vec<String>>,
647 #[serde(skip_serializing_if = "Option::is_none")]
649 pub model: Option<String>,
650}
651
652#[derive(Clone, Default)]
658pub struct ClaudeAgentOptions {
659 pub allowed_tools: Vec<ToolName>,
661 pub system_prompt: Option<SystemPrompt>,
663 pub mcp_servers: McpServers,
665 pub permission_mode: Option<PermissionMode>,
667 pub continue_conversation: bool,
669 pub resume: Option<SessionId>,
671 pub max_turns: Option<u32>,
673 pub disallowed_tools: Vec<ToolName>,
675 pub model: Option<String>,
677 pub permission_prompt_tool_name: Option<String>,
679 pub cwd: Option<PathBuf>,
681 pub settings: Option<PathBuf>,
683 pub add_dirs: Vec<PathBuf>,
685 pub env: HashMap<String, String>,
687 pub extra_args: HashMap<String, Option<String>>,
689 pub max_buffer_size: Option<usize>,
691 pub can_use_tool: Option<CanUseToolCallback>,
693 pub hooks: Option<HashMap<HookEvent, Vec<HookMatcher>>>,
695 pub user: Option<String>,
697 pub include_partial_messages: bool,
699 pub fork_session: bool,
701 pub agents: Option<HashMap<String, AgentDefinition>>,
703 pub setting_sources: Option<Vec<SettingSource>>,
705}
706
707impl ClaudeAgentOptions {
708 pub fn builder() -> ClaudeAgentOptionsBuilder {
710 ClaudeAgentOptionsBuilder::default()
711 }
712}
713
714impl std::fmt::Debug for ClaudeAgentOptions {
715 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
716 f.debug_struct("ClaudeAgentOptions")
717 .field("allowed_tools", &self.allowed_tools)
718 .field("system_prompt", &self.system_prompt)
719 .field("mcp_servers", &self.mcp_servers)
720 .field("permission_mode", &self.permission_mode)
721 .field("continue_conversation", &self.continue_conversation)
722 .field("resume", &self.resume)
723 .field("max_turns", &self.max_turns)
724 .field("disallowed_tools", &self.disallowed_tools)
725 .field("model", &self.model)
726 .field(
727 "permission_prompt_tool_name",
728 &self.permission_prompt_tool_name,
729 )
730 .field("cwd", &self.cwd)
731 .field("settings", &self.settings)
732 .field("add_dirs", &self.add_dirs)
733 .field("env", &self.env)
734 .field("extra_args", &self.extra_args)
735 .field("max_buffer_size", &self.max_buffer_size)
736 .field(
737 "can_use_tool",
738 &self.can_use_tool.as_ref().map(|_| "<callback>"),
739 )
740 .field(
741 "hooks",
742 &self
743 .hooks
744 .as_ref()
745 .map(|h| format!("[{} hook types]", h.len())),
746 )
747 .field("user", &self.user)
748 .field("include_partial_messages", &self.include_partial_messages)
749 .field("fork_session", &self.fork_session)
750 .field("agents", &self.agents)
751 .field("setting_sources", &self.setting_sources)
752 .finish()
753 }
754}
755
756#[derive(Debug, Default)]
762pub struct ClaudeAgentOptionsBuilder {
763 options: ClaudeAgentOptions,
764}
765
766impl ClaudeAgentOptionsBuilder {
767 pub fn allowed_tools(mut self, tools: Vec<impl Into<ToolName>>) -> Self {
769 self.options.allowed_tools = tools.into_iter().map(|t| t.into()).collect();
770 self
771 }
772
773 pub fn add_allowed_tool(mut self, tool: impl Into<ToolName>) -> Self {
775 self.options.allowed_tools.push(tool.into());
776 self
777 }
778
779 pub fn system_prompt(mut self, prompt: impl Into<SystemPrompt>) -> Self {
781 self.options.system_prompt = Some(prompt.into());
782 self
783 }
784
785 pub fn mcp_servers(mut self, servers: HashMap<String, McpServerConfig>) -> Self {
787 self.options.mcp_servers = McpServers::Dict(servers);
788 self
789 }
790
791 pub fn permission_mode(mut self, mode: PermissionMode) -> Self {
793 self.options.permission_mode = Some(mode);
794 self
795 }
796
797 pub fn max_turns(mut self, turns: u32) -> Self {
799 const MAX_ALLOWED_TURNS: u32 = 1000;
800 if turns > MAX_ALLOWED_TURNS {
801 panic!(
802 "max_turns {turns} exceeds maximum allowed: {MAX_ALLOWED_TURNS}"
803 );
804 }
805 self.options.max_turns = Some(turns);
806 self
807 }
808
809 pub fn cwd(mut self, path: impl Into<PathBuf>) -> Self {
811 self.options.cwd = Some(path.into());
812 self
813 }
814
815 pub fn can_use_tool(mut self, callback: CanUseToolCallback) -> Self {
817 self.options.can_use_tool = Some(callback);
818 self
819 }
820
821 pub fn hooks(mut self, hooks: HashMap<HookEvent, Vec<HookMatcher>>) -> Self {
823 self.options.hooks = Some(hooks);
824 self
825 }
826
827 pub fn build(self) -> ClaudeAgentOptions {
829 self.options
830 }
831}
832
833impl From<String> for SystemPrompt {
835 fn from(s: String) -> Self {
836 SystemPrompt::String(s)
837 }
838}
839
840impl From<&str> for SystemPrompt {
841 fn from(s: &str) -> Self {
842 SystemPrompt::String(s.to_string())
843 }
844}
845
846impl From<SystemPromptPreset> for SystemPrompt {
847 fn from(preset: SystemPromptPreset) -> Self {
848 SystemPrompt::Preset(preset)
849 }
850}