1#![allow(missing_docs)]
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::path::PathBuf;
10use std::sync::Arc;
11use async_trait::async_trait;
12use std::io::Write;
13use tokio::sync::Mutex;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
17#[serde(rename_all = "camelCase")]
18pub enum PermissionMode {
19 Default,
21 AcceptEdits,
23 Plan,
25 BypassPermissions,
27}
28
29impl Default for PermissionMode {
30 fn default() -> Self {
31 Self::Default
32 }
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum ControlProtocolFormat {
38 Legacy,
40 Control,
42 Auto,
44}
45
46impl Default for ControlProtocolFormat {
47 fn default() -> Self {
48 Self::Legacy
50 }
51}
52
53#[derive(Clone)]
55pub enum McpServerConfig {
56 Stdio {
58 command: String,
60 args: Option<Vec<String>>,
62 env: Option<HashMap<String, String>>,
64 },
65 Sse {
67 url: String,
69 headers: Option<HashMap<String, String>>,
71 },
72 Http {
74 url: String,
76 headers: Option<HashMap<String, String>>,
78 },
79 Sdk {
81 name: String,
83 instance: Arc<dyn std::any::Any + Send + Sync>,
85 },
86}
87
88impl std::fmt::Debug for McpServerConfig {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 match self {
91 Self::Stdio { command, args, env } => f
92 .debug_struct("Stdio")
93 .field("command", command)
94 .field("args", args)
95 .field("env", env)
96 .finish(),
97 Self::Sse { url, headers } => f
98 .debug_struct("Sse")
99 .field("url", url)
100 .field("headers", headers)
101 .finish(),
102 Self::Http { url, headers } => f
103 .debug_struct("Http")
104 .field("url", url)
105 .field("headers", headers)
106 .finish(),
107 Self::Sdk { name, .. } => f
108 .debug_struct("Sdk")
109 .field("name", name)
110 .field("instance", &"<Arc<dyn Any>>")
111 .finish(),
112 }
113 }
114}
115
116impl Serialize for McpServerConfig {
117 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
118 where
119 S: serde::Serializer,
120 {
121 use serde::ser::SerializeMap;
122 let mut map = serializer.serialize_map(None)?;
123
124 match self {
125 Self::Stdio { command, args, env } => {
126 map.serialize_entry("type", "stdio")?;
127 map.serialize_entry("command", command)?;
128 if let Some(args) = args {
129 map.serialize_entry("args", args)?;
130 }
131 if let Some(env) = env {
132 map.serialize_entry("env", env)?;
133 }
134 }
135 Self::Sse { url, headers } => {
136 map.serialize_entry("type", "sse")?;
137 map.serialize_entry("url", url)?;
138 if let Some(headers) = headers {
139 map.serialize_entry("headers", headers)?;
140 }
141 }
142 Self::Http { url, headers } => {
143 map.serialize_entry("type", "http")?;
144 map.serialize_entry("url", url)?;
145 if let Some(headers) = headers {
146 map.serialize_entry("headers", headers)?;
147 }
148 }
149 Self::Sdk { name, .. } => {
150 map.serialize_entry("type", "sdk")?;
151 map.serialize_entry("name", name)?;
152 }
153 }
154
155 map.end()
156 }
157}
158
159impl<'de> Deserialize<'de> for McpServerConfig {
160 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
161 where
162 D: serde::Deserializer<'de>,
163 {
164 #[derive(Deserialize)]
165 #[serde(tag = "type", rename_all = "lowercase")]
166 enum McpServerConfigHelper {
167 Stdio {
168 command: String,
169 #[serde(skip_serializing_if = "Option::is_none")]
170 args: Option<Vec<String>>,
171 #[serde(skip_serializing_if = "Option::is_none")]
172 env: Option<HashMap<String, String>>,
173 },
174 Sse {
175 url: String,
176 #[serde(skip_serializing_if = "Option::is_none")]
177 headers: Option<HashMap<String, String>>,
178 },
179 Http {
180 url: String,
181 #[serde(skip_serializing_if = "Option::is_none")]
182 headers: Option<HashMap<String, String>>,
183 },
184 }
185
186 let helper = McpServerConfigHelper::deserialize(deserializer)?;
187 Ok(match helper {
188 McpServerConfigHelper::Stdio { command, args, env } => {
189 McpServerConfig::Stdio { command, args, env }
190 }
191 McpServerConfigHelper::Sse { url, headers } => {
192 McpServerConfig::Sse { url, headers }
193 }
194 McpServerConfigHelper::Http { url, headers } => {
195 McpServerConfig::Http { url, headers }
196 }
197 })
198 }
199}
200
201#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
203#[serde(rename_all = "camelCase")]
204pub enum PermissionUpdateDestination {
205 UserSettings,
207 ProjectSettings,
209 LocalSettings,
211 Session,
213}
214
215#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
217#[serde(rename_all = "camelCase")]
218pub enum PermissionBehavior {
219 Allow,
221 Deny,
223 Ask,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize)]
229pub struct PermissionRuleValue {
230 pub tool_name: String,
232 pub rule_content: Option<String>,
234}
235
236#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
238#[serde(rename_all = "camelCase")]
239pub enum PermissionUpdateType {
240 AddRules,
242 ReplaceRules,
244 RemoveRules,
246 SetMode,
248 AddDirectories,
250 RemoveDirectories,
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize)]
256#[serde(rename_all = "camelCase")]
257pub struct PermissionUpdate {
258 #[serde(rename = "type")]
260 pub update_type: PermissionUpdateType,
261 #[serde(skip_serializing_if = "Option::is_none")]
263 pub rules: Option<Vec<PermissionRuleValue>>,
264 #[serde(skip_serializing_if = "Option::is_none")]
266 pub behavior: Option<PermissionBehavior>,
267 #[serde(skip_serializing_if = "Option::is_none")]
269 pub mode: Option<PermissionMode>,
270 #[serde(skip_serializing_if = "Option::is_none")]
272 pub directories: Option<Vec<String>>,
273 #[serde(skip_serializing_if = "Option::is_none")]
275 pub destination: Option<PermissionUpdateDestination>,
276}
277
278#[derive(Debug, Clone)]
280pub struct ToolPermissionContext {
281 pub signal: Option<Arc<dyn std::any::Any + Send + Sync>>,
283 pub suggestions: Vec<PermissionUpdate>,
285}
286
287#[derive(Debug, Clone)]
289pub struct PermissionResultAllow {
290 pub updated_input: Option<serde_json::Value>,
292 pub updated_permissions: Option<Vec<PermissionUpdate>>,
294}
295
296#[derive(Debug, Clone)]
298pub struct PermissionResultDeny {
299 pub message: String,
301 pub interrupt: bool,
303}
304
305#[derive(Debug, Clone)]
307pub enum PermissionResult {
308 Allow(PermissionResultAllow),
310 Deny(PermissionResultDeny),
312}
313
314#[async_trait]
316pub trait CanUseTool: Send + Sync {
317 async fn can_use_tool(
319 &self,
320 tool_name: &str,
321 input: &serde_json::Value,
322 context: &ToolPermissionContext,
323 ) -> PermissionResult;
324}
325
326#[derive(Debug, Clone)]
328pub struct HookContext {
329 pub signal: Option<Arc<dyn std::any::Any + Send + Sync>>,
331}
332
333#[derive(Debug, Clone, Serialize, Deserialize)]
339pub struct BaseHookInput {
340 pub session_id: String,
342 pub transcript_path: String,
344 pub cwd: String,
346 #[serde(skip_serializing_if = "Option::is_none")]
348 pub permission_mode: Option<String>,
349}
350
351#[derive(Debug, Clone, Serialize, Deserialize)]
353pub struct PreToolUseHookInput {
354 pub session_id: String,
356 pub transcript_path: String,
358 pub cwd: String,
360 #[serde(skip_serializing_if = "Option::is_none")]
362 pub permission_mode: Option<String>,
363 pub tool_name: String,
365 pub tool_input: serde_json::Value,
367}
368
369#[derive(Debug, Clone, Serialize, Deserialize)]
371pub struct PostToolUseHookInput {
372 pub session_id: String,
374 pub transcript_path: String,
376 pub cwd: String,
378 #[serde(skip_serializing_if = "Option::is_none")]
380 pub permission_mode: Option<String>,
381 pub tool_name: String,
383 pub tool_input: serde_json::Value,
385 pub tool_response: serde_json::Value,
387}
388
389#[derive(Debug, Clone, Serialize, Deserialize)]
391pub struct UserPromptSubmitHookInput {
392 pub session_id: String,
394 pub transcript_path: String,
396 pub cwd: String,
398 #[serde(skip_serializing_if = "Option::is_none")]
400 pub permission_mode: Option<String>,
401 pub prompt: String,
403}
404
405#[derive(Debug, Clone, Serialize, Deserialize)]
407pub struct StopHookInput {
408 pub session_id: String,
410 pub transcript_path: String,
412 pub cwd: String,
414 #[serde(skip_serializing_if = "Option::is_none")]
416 pub permission_mode: Option<String>,
417 pub stop_hook_active: bool,
419}
420
421#[derive(Debug, Clone, Serialize, Deserialize)]
423pub struct SubagentStopHookInput {
424 pub session_id: String,
426 pub transcript_path: String,
428 pub cwd: String,
430 #[serde(skip_serializing_if = "Option::is_none")]
432 pub permission_mode: Option<String>,
433 pub stop_hook_active: bool,
435}
436
437#[derive(Debug, Clone, Serialize, Deserialize)]
439pub struct PreCompactHookInput {
440 pub session_id: String,
442 pub transcript_path: String,
444 pub cwd: String,
446 #[serde(skip_serializing_if = "Option::is_none")]
448 pub permission_mode: Option<String>,
449 pub trigger: String,
451 #[serde(skip_serializing_if = "Option::is_none")]
453 pub custom_instructions: Option<String>,
454}
455
456#[derive(Debug, Clone, Serialize, Deserialize)]
458#[serde(tag = "hook_event_name")]
459pub enum HookInput {
460 #[serde(rename = "PreToolUse")]
462 PreToolUse(PreToolUseHookInput),
463 #[serde(rename = "PostToolUse")]
465 PostToolUse(PostToolUseHookInput),
466 #[serde(rename = "UserPromptSubmit")]
468 UserPromptSubmit(UserPromptSubmitHookInput),
469 #[serde(rename = "Stop")]
471 Stop(StopHookInput),
472 #[serde(rename = "SubagentStop")]
474 SubagentStop(SubagentStopHookInput),
475 #[serde(rename = "PreCompact")]
477 PreCompact(PreCompactHookInput),
478}
479
480#[derive(Debug, Clone, Serialize, Deserialize)]
489pub struct AsyncHookJSONOutput {
490 #[serde(rename = "async")]
492 pub async_: bool,
493 #[serde(skip_serializing_if = "Option::is_none")]
495 #[serde(rename = "asyncTimeout")]
496 pub async_timeout: Option<u32>,
497}
498
499#[derive(Debug, Clone, Default, Serialize, Deserialize)]
504pub struct SyncHookJSONOutput {
505 #[serde(rename = "continue", skip_serializing_if = "Option::is_none")]
508 pub continue_: Option<bool>,
509 #[serde(rename = "suppressOutput", skip_serializing_if = "Option::is_none")]
511 pub suppress_output: Option<bool>,
512 #[serde(rename = "stopReason", skip_serializing_if = "Option::is_none")]
514 pub stop_reason: Option<String>,
515
516 #[serde(skip_serializing_if = "Option::is_none")]
519 pub decision: Option<String>, #[serde(rename = "systemMessage", skip_serializing_if = "Option::is_none")]
522 pub system_message: Option<String>,
523 #[serde(skip_serializing_if = "Option::is_none")]
525 pub reason: Option<String>,
526
527 #[serde(rename = "hookSpecificOutput", skip_serializing_if = "Option::is_none")]
530 pub hook_specific_output: Option<HookSpecificOutput>,
531}
532
533#[derive(Debug, Clone, Serialize, Deserialize)]
535#[serde(untagged)]
536pub enum HookJSONOutput {
537 Async(AsyncHookJSONOutput),
539 Sync(SyncHookJSONOutput),
541}
542
543#[derive(Debug, Clone, Serialize, Deserialize)]
545pub struct PreToolUseHookSpecificOutput {
546 #[serde(rename = "permissionDecision", skip_serializing_if = "Option::is_none")]
548 pub permission_decision: Option<String>,
549 #[serde(rename = "permissionDecisionReason", skip_serializing_if = "Option::is_none")]
551 pub permission_decision_reason: Option<String>,
552 #[serde(rename = "updatedInput", skip_serializing_if = "Option::is_none")]
554 pub updated_input: Option<serde_json::Value>,
555}
556
557#[derive(Debug, Clone, Serialize, Deserialize)]
559pub struct PostToolUseHookSpecificOutput {
560 #[serde(rename = "additionalContext", skip_serializing_if = "Option::is_none")]
562 pub additional_context: Option<String>,
563}
564
565#[derive(Debug, Clone, Serialize, Deserialize)]
567pub struct UserPromptSubmitHookSpecificOutput {
568 #[serde(rename = "additionalContext", skip_serializing_if = "Option::is_none")]
570 pub additional_context: Option<String>,
571}
572
573#[derive(Debug, Clone, Serialize, Deserialize)]
575pub struct SessionStartHookSpecificOutput {
576 #[serde(rename = "additionalContext", skip_serializing_if = "Option::is_none")]
578 pub additional_context: Option<String>,
579}
580
581#[derive(Debug, Clone, Serialize, Deserialize)]
583#[serde(tag = "hookEventName")]
584pub enum HookSpecificOutput {
585 #[serde(rename = "PreToolUse")]
587 PreToolUse(PreToolUseHookSpecificOutput),
588 #[serde(rename = "PostToolUse")]
590 PostToolUse(PostToolUseHookSpecificOutput),
591 #[serde(rename = "UserPromptSubmit")]
593 UserPromptSubmit(UserPromptSubmitHookSpecificOutput),
594 #[serde(rename = "SessionStart")]
596 SessionStart(SessionStartHookSpecificOutput),
597}
598
599#[async_trait]
608pub trait HookCallback: Send + Sync {
609 async fn execute(
621 &self,
622 input: &HookInput,
623 tool_use_id: Option<&str>,
624 context: &HookContext,
625 ) -> Result<HookJSONOutput, crate::errors::SdkError>;
626}
627
628#[deprecated(
633 since = "0.3.0",
634 note = "Use the new HookCallback trait with HookInput/HookJSONOutput instead"
635)]
636#[allow(dead_code)]
637#[async_trait]
638pub trait HookCallbackLegacy: Send + Sync {
639 async fn execute_legacy(
641 &self,
642 input: &serde_json::Value,
643 tool_use_id: Option<&str>,
644 context: &HookContext,
645 ) -> serde_json::Value;
646}
647
648#[derive(Clone)]
650pub struct HookMatcher {
651 pub matcher: Option<serde_json::Value>,
653 pub hooks: Vec<Arc<dyn HookCallback>>,
655}
656
657#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
659#[serde(rename_all = "lowercase")]
660pub enum SettingSource {
661 User,
663 Project,
665 Local,
667}
668
669#[derive(Debug, Clone, Serialize, Deserialize)]
671pub struct AgentDefinition {
672 pub description: String,
674 pub prompt: String,
676 #[serde(skip_serializing_if = "Option::is_none")]
678 pub tools: Option<Vec<String>>,
679 #[serde(skip_serializing_if = "Option::is_none")]
681 pub model: Option<String>,
682}
683
684#[derive(Debug, Clone, Serialize, Deserialize)]
686#[serde(untagged)]
687pub enum SystemPrompt {
688 String(String),
690 Preset {
692 #[serde(rename = "type")]
693 preset_type: String, preset: String, #[serde(skip_serializing_if = "Option::is_none")]
696 append: Option<String>,
697 },
698}
699
700#[derive(Clone, Default)]
702pub struct ClaudeCodeOptions {
703 pub system_prompt_v2: Option<SystemPrompt>,
707 #[deprecated(since = "0.1.12", note = "Use system_prompt_v2 instead")]
710 pub system_prompt: Option<String>,
711 #[deprecated(since = "0.1.12", note = "Use system_prompt_v2 instead")]
714 pub append_system_prompt: Option<String>,
715 pub allowed_tools: Vec<String>,
717 pub disallowed_tools: Vec<String>,
719 pub permission_mode: PermissionMode,
721 pub mcp_servers: HashMap<String, McpServerConfig>,
723 pub mcp_tools: Vec<String>,
725 pub max_turns: Option<i32>,
727 pub max_thinking_tokens: i32,
729 pub max_output_tokens: Option<u32>,
731 pub model: Option<String>,
733 pub cwd: Option<PathBuf>,
735 pub continue_conversation: bool,
737 pub resume: Option<String>,
739 pub permission_prompt_tool_name: Option<String>,
741 pub settings: Option<String>,
743 pub add_dirs: Vec<PathBuf>,
745 pub extra_args: HashMap<String, Option<String>>,
747 pub env: HashMap<String, String>,
749 pub debug_stderr: Option<Arc<Mutex<dyn Write + Send + Sync>>>,
751 pub include_partial_messages: bool,
753 pub can_use_tool: Option<Arc<dyn CanUseTool>>,
755 pub hooks: Option<HashMap<String, Vec<HookMatcher>>>,
757 pub control_protocol_format: ControlProtocolFormat,
759
760 pub setting_sources: Option<Vec<SettingSource>>,
764 pub fork_session: bool,
767 pub agents: Option<HashMap<String, AgentDefinition>>,
770 pub cli_channel_buffer_size: Option<usize>,
774}
775
776impl std::fmt::Debug for ClaudeCodeOptions {
777 #[allow(deprecated)]
778 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
779 f.debug_struct("ClaudeCodeOptions")
780 .field("system_prompt", &self.system_prompt)
781 .field("append_system_prompt", &self.append_system_prompt)
782 .field("allowed_tools", &self.allowed_tools)
783 .field("disallowed_tools", &self.disallowed_tools)
784 .field("permission_mode", &self.permission_mode)
785 .field("mcp_servers", &self.mcp_servers)
786 .field("mcp_tools", &self.mcp_tools)
787 .field("max_turns", &self.max_turns)
788 .field("max_thinking_tokens", &self.max_thinking_tokens)
789 .field("max_output_tokens", &self.max_output_tokens)
790 .field("model", &self.model)
791 .field("cwd", &self.cwd)
792 .field("continue_conversation", &self.continue_conversation)
793 .field("resume", &self.resume)
794 .field("permission_prompt_tool_name", &self.permission_prompt_tool_name)
795 .field("settings", &self.settings)
796 .field("add_dirs", &self.add_dirs)
797 .field("extra_args", &self.extra_args)
798 .field("env", &self.env)
799 .field("debug_stderr", &self.debug_stderr.is_some())
800 .field("include_partial_messages", &self.include_partial_messages)
801 .field("can_use_tool", &self.can_use_tool.is_some())
802 .field("hooks", &self.hooks.is_some())
803 .field("control_protocol_format", &self.control_protocol_format)
804 .finish()
805 }
806}
807
808impl ClaudeCodeOptions {
809 pub fn builder() -> ClaudeCodeOptionsBuilder {
811 ClaudeCodeOptionsBuilder::default()
812 }
813}
814
815#[derive(Debug, Default)]
817pub struct ClaudeCodeOptionsBuilder {
818 options: ClaudeCodeOptions,
819}
820
821impl ClaudeCodeOptionsBuilder {
822 #[allow(deprecated)]
824 pub fn system_prompt(mut self, prompt: impl Into<String>) -> Self {
825 self.options.system_prompt = Some(prompt.into());
826 self
827 }
828
829 #[allow(deprecated)]
831 pub fn append_system_prompt(mut self, prompt: impl Into<String>) -> Self {
832 self.options.append_system_prompt = Some(prompt.into());
833 self
834 }
835
836 pub fn allowed_tools(mut self, tools: Vec<String>) -> Self {
838 self.options.allowed_tools = tools;
839 self
840 }
841
842 pub fn allow_tool(mut self, tool: impl Into<String>) -> Self {
844 self.options.allowed_tools.push(tool.into());
845 self
846 }
847
848 pub fn disallowed_tools(mut self, tools: Vec<String>) -> Self {
850 self.options.disallowed_tools = tools;
851 self
852 }
853
854 pub fn disallow_tool(mut self, tool: impl Into<String>) -> Self {
856 self.options.disallowed_tools.push(tool.into());
857 self
858 }
859
860 pub fn permission_mode(mut self, mode: PermissionMode) -> Self {
862 self.options.permission_mode = mode;
863 self
864 }
865
866 pub fn add_mcp_server(mut self, name: impl Into<String>, config: McpServerConfig) -> Self {
868 self.options.mcp_servers.insert(name.into(), config);
869 self
870 }
871
872 pub fn mcp_servers(mut self, servers: HashMap<String, McpServerConfig>) -> Self {
874 self.options.mcp_servers = servers;
875 self
876 }
877
878 pub fn mcp_tools(mut self, tools: Vec<String>) -> Self {
880 self.options.mcp_tools = tools;
881 self
882 }
883
884 pub fn max_turns(mut self, turns: i32) -> Self {
886 self.options.max_turns = Some(turns);
887 self
888 }
889
890 pub fn max_thinking_tokens(mut self, tokens: i32) -> Self {
892 self.options.max_thinking_tokens = tokens;
893 self
894 }
895
896 pub fn max_output_tokens(mut self, tokens: u32) -> Self {
898 self.options.max_output_tokens = Some(tokens.clamp(1, 32000));
899 self
900 }
901
902 pub fn model(mut self, model: impl Into<String>) -> Self {
904 self.options.model = Some(model.into());
905 self
906 }
907
908 pub fn cwd(mut self, path: impl Into<PathBuf>) -> Self {
910 self.options.cwd = Some(path.into());
911 self
912 }
913
914 pub fn continue_conversation(mut self, enable: bool) -> Self {
916 self.options.continue_conversation = enable;
917 self
918 }
919
920 pub fn resume(mut self, id: impl Into<String>) -> Self {
922 self.options.resume = Some(id.into());
923 self
924 }
925
926 pub fn permission_prompt_tool_name(mut self, name: impl Into<String>) -> Self {
928 self.options.permission_prompt_tool_name = Some(name.into());
929 self
930 }
931
932 pub fn settings(mut self, settings: impl Into<String>) -> Self {
934 self.options.settings = Some(settings.into());
935 self
936 }
937
938 pub fn add_dirs(mut self, dirs: Vec<PathBuf>) -> Self {
940 self.options.add_dirs = dirs;
941 self
942 }
943
944 pub fn add_dir(mut self, dir: impl Into<PathBuf>) -> Self {
946 self.options.add_dirs.push(dir.into());
947 self
948 }
949
950 pub fn extra_args(mut self, args: HashMap<String, Option<String>>) -> Self {
952 self.options.extra_args = args;
953 self
954 }
955
956 pub fn add_extra_arg(mut self, key: impl Into<String>, value: Option<String>) -> Self {
958 self.options.extra_args.insert(key.into(), value);
959 self
960 }
961
962 pub fn control_protocol_format(mut self, format: ControlProtocolFormat) -> Self {
964 self.options.control_protocol_format = format;
965 self
966 }
967
968 pub fn include_partial_messages(mut self, include: bool) -> Self {
970 self.options.include_partial_messages = include;
971 self
972 }
973
974 pub fn fork_session(mut self, fork: bool) -> Self {
976 self.options.fork_session = fork;
977 self
978 }
979
980 pub fn setting_sources(mut self, sources: Vec<SettingSource>) -> Self {
982 self.options.setting_sources = Some(sources);
983 self
984 }
985
986 pub fn agents(mut self, agents: HashMap<String, AgentDefinition>) -> Self {
988 self.options.agents = Some(agents);
989 self
990 }
991
992 pub fn cli_channel_buffer_size(mut self, size: usize) -> Self {
1010 self.options.cli_channel_buffer_size = Some(size);
1011 self
1012 }
1013
1014 pub fn build(self) -> ClaudeCodeOptions {
1016 self.options
1017 }
1018}
1019
1020#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1022#[serde(tag = "type", rename_all = "lowercase")]
1023pub enum Message {
1024 User {
1026 message: UserMessage,
1028 },
1029 Assistant {
1031 message: AssistantMessage,
1033 },
1034 System {
1036 subtype: String,
1038 data: serde_json::Value,
1040 },
1041 Result {
1043 subtype: String,
1045 duration_ms: i64,
1047 duration_api_ms: i64,
1049 is_error: bool,
1051 num_turns: i32,
1053 session_id: String,
1055 #[serde(skip_serializing_if = "Option::is_none")]
1057 total_cost_usd: Option<f64>,
1058 #[serde(skip_serializing_if = "Option::is_none")]
1060 usage: Option<serde_json::Value>,
1061 #[serde(skip_serializing_if = "Option::is_none")]
1063 result: Option<String>,
1064 },
1065}
1066
1067#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1069pub struct UserMessage {
1070 pub content: String,
1072}
1073
1074#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1076pub struct AssistantMessage {
1077 pub content: Vec<ContentBlock>,
1079}
1080
1081pub use Message::Result as ResultMessage;
1083pub use Message::System as SystemMessage;
1085
1086#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1088#[serde(untagged)]
1089pub enum ContentBlock {
1090 Text(TextContent),
1092 Thinking(ThinkingContent),
1094 ToolUse(ToolUseContent),
1096 ToolResult(ToolResultContent),
1098}
1099
1100#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1102pub struct TextContent {
1103 pub text: String,
1105}
1106
1107#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1109pub struct ThinkingContent {
1110 pub thinking: String,
1112 pub signature: String,
1114}
1115
1116#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1118pub struct ToolUseContent {
1119 pub id: String,
1121 pub name: String,
1123 pub input: serde_json::Value,
1125}
1126
1127#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1129pub struct ToolResultContent {
1130 pub tool_use_id: String,
1132 #[serde(skip_serializing_if = "Option::is_none")]
1134 pub content: Option<ContentValue>,
1135 #[serde(skip_serializing_if = "Option::is_none")]
1137 pub is_error: Option<bool>,
1138}
1139
1140#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1142#[serde(untagged)]
1143pub enum ContentValue {
1144 Text(String),
1146 Structured(Vec<serde_json::Value>),
1148}
1149
1150#[derive(Debug, Clone, Serialize, Deserialize)]
1152pub struct UserContent {
1153 pub role: String,
1155 pub content: String,
1157}
1158
1159#[derive(Debug, Clone, Serialize, Deserialize)]
1161pub struct AssistantContent {
1162 pub role: String,
1164 pub content: Vec<ContentBlock>,
1166}
1167
1168#[derive(Debug, Clone, Serialize, Deserialize)]
1170pub struct SDKControlInterruptRequest {
1171 pub subtype: String, }
1174
1175#[derive(Debug, Clone, Serialize, Deserialize)]
1177#[serde(rename_all = "camelCase")]
1178pub struct SDKControlPermissionRequest {
1179 pub subtype: String, pub tool_name: String,
1183 pub input: serde_json::Value,
1185 #[serde(skip_serializing_if = "Option::is_none")]
1187 pub permission_suggestions: Option<Vec<PermissionUpdate>>,
1188 #[serde(skip_serializing_if = "Option::is_none")]
1190 pub blocked_path: Option<String>,
1191}
1192
1193#[derive(Debug, Clone, Serialize, Deserialize)]
1195pub struct SDKControlInitializeRequest {
1196 pub subtype: String, #[serde(skip_serializing_if = "Option::is_none")]
1200 pub hooks: Option<HashMap<String, serde_json::Value>>,
1201}
1202
1203#[derive(Debug, Clone, Serialize, Deserialize)]
1205#[serde(rename_all = "camelCase")]
1206pub struct SDKControlSetPermissionModeRequest {
1207 pub subtype: String, pub mode: String,
1211}
1212
1213#[derive(Debug, Clone, Serialize, Deserialize)]
1215#[serde(rename_all = "camelCase")]
1216pub struct SDKControlSetModelRequest {
1217 pub subtype: String, #[serde(skip_serializing_if = "Option::is_none")]
1221 pub model: Option<String>,
1222}
1223
1224#[derive(Debug, Clone, Serialize, Deserialize)]
1226#[serde(rename_all = "camelCase")]
1227pub struct SDKHookCallbackRequest {
1228 pub subtype: String, pub callback_id: String,
1232 pub input: serde_json::Value,
1234 #[serde(skip_serializing_if = "Option::is_none")]
1236 pub tool_use_id: Option<String>,
1237}
1238
1239#[derive(Debug, Clone, Serialize, Deserialize)]
1241#[serde(rename_all = "camelCase")]
1242pub struct SDKControlMcpMessageRequest {
1243 pub subtype: String, pub mcp_server_name: String,
1247 pub message: serde_json::Value,
1249}
1250
1251#[derive(Debug, Clone, Serialize, Deserialize)]
1253#[serde(tag = "type", rename_all = "snake_case")]
1254pub enum SDKControlRequest {
1255 #[serde(rename = "interrupt")]
1257 Interrupt(SDKControlInterruptRequest),
1258 #[serde(rename = "can_use_tool")]
1260 CanUseTool(SDKControlPermissionRequest),
1261 #[serde(rename = "initialize")]
1263 Initialize(SDKControlInitializeRequest),
1264 #[serde(rename = "set_permission_mode")]
1266 SetPermissionMode(SDKControlSetPermissionModeRequest),
1267 #[serde(rename = "set_model")]
1269 SetModel(SDKControlSetModelRequest),
1270 #[serde(rename = "hook_callback")]
1272 HookCallback(SDKHookCallbackRequest),
1273 #[serde(rename = "mcp_message")]
1275 McpMessage(SDKControlMcpMessageRequest),
1276}
1277
1278#[derive(Debug, Clone, Serialize, Deserialize)]
1280#[serde(tag = "type", rename_all = "lowercase")]
1281pub enum ControlRequest {
1282 Interrupt {
1284 request_id: String,
1286 },
1287}
1288
1289#[derive(Debug, Clone, Serialize, Deserialize)]
1291#[serde(tag = "type", rename_all = "lowercase")]
1292pub enum ControlResponse {
1293 InterruptAck {
1295 request_id: String,
1297 success: bool,
1299 },
1300}
1301
1302#[cfg(test)]
1303mod tests {
1304 use super::*;
1305
1306 #[test]
1307 fn test_permission_mode_serialization() {
1308 let mode = PermissionMode::AcceptEdits;
1309 let json = serde_json::to_string(&mode).unwrap();
1310 assert_eq!(json, r#""acceptEdits""#);
1311
1312 let deserialized: PermissionMode = serde_json::from_str(&json).unwrap();
1313 assert_eq!(deserialized, mode);
1314
1315 let plan_mode = PermissionMode::Plan;
1317 let plan_json = serde_json::to_string(&plan_mode).unwrap();
1318 assert_eq!(plan_json, r#""plan""#);
1319
1320 let plan_deserialized: PermissionMode = serde_json::from_str(&plan_json).unwrap();
1321 assert_eq!(plan_deserialized, plan_mode);
1322 }
1323
1324 #[test]
1325 fn test_message_serialization() {
1326 let msg = Message::User {
1327 message: UserMessage {
1328 content: "Hello".to_string(),
1329 },
1330 };
1331
1332 let json = serde_json::to_string(&msg).unwrap();
1333 assert!(json.contains(r#""type":"user""#));
1334 assert!(json.contains(r#""content":"Hello""#));
1335
1336 let deserialized: Message = serde_json::from_str(&json).unwrap();
1337 assert_eq!(deserialized, msg);
1338 }
1339
1340 #[test]
1341 #[allow(deprecated)]
1342 fn test_options_builder() {
1343 let options = ClaudeCodeOptions::builder()
1344 .system_prompt("Test prompt")
1345 .model("claude-3-opus")
1346 .permission_mode(PermissionMode::AcceptEdits)
1347 .allow_tool("read")
1348 .allow_tool("write")
1349 .max_turns(10)
1350 .build();
1351
1352 assert_eq!(options.system_prompt, Some("Test prompt".to_string()));
1353 assert_eq!(options.model, Some("claude-3-opus".to_string()));
1354 assert_eq!(options.permission_mode, PermissionMode::AcceptEdits);
1355 assert_eq!(options.allowed_tools, vec!["read", "write"]);
1356 assert_eq!(options.max_turns, Some(10));
1357 }
1358
1359 #[test]
1360 fn test_extra_args() {
1361 let mut extra_args = HashMap::new();
1362 extra_args.insert("custom-flag".to_string(), Some("value".to_string()));
1363 extra_args.insert("boolean-flag".to_string(), None);
1364
1365 let options = ClaudeCodeOptions::builder()
1366 .extra_args(extra_args.clone())
1367 .add_extra_arg("another-flag", Some("another-value".to_string()))
1368 .build();
1369
1370 assert_eq!(options.extra_args.len(), 3);
1371 assert_eq!(options.extra_args.get("custom-flag"), Some(&Some("value".to_string())));
1372 assert_eq!(options.extra_args.get("boolean-flag"), Some(&None));
1373 assert_eq!(options.extra_args.get("another-flag"), Some(&Some("another-value".to_string())));
1374 }
1375
1376 #[test]
1377 fn test_thinking_content_serialization() {
1378 let thinking = ThinkingContent {
1379 thinking: "Let me think about this...".to_string(),
1380 signature: "sig123".to_string(),
1381 };
1382
1383 let json = serde_json::to_string(&thinking).unwrap();
1384 assert!(json.contains(r#""thinking":"Let me think about this...""#));
1385 assert!(json.contains(r#""signature":"sig123""#));
1386
1387 let deserialized: ThinkingContent = serde_json::from_str(&json).unwrap();
1388 assert_eq!(deserialized.thinking, thinking.thinking);
1389 assert_eq!(deserialized.signature, thinking.signature);
1390 }
1391}