cc_sdk/
types.rs

1//! Type definitions for the Claude Code SDK
2//!
3//! This module contains all the core types used throughout the SDK,
4//! including messages, configuration options, and content blocks.
5
6#![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/// Permission mode for tool execution
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
17#[serde(rename_all = "camelCase")]
18pub enum PermissionMode {
19    /// Default mode - CLI prompts for dangerous tools
20    Default,
21    /// Auto-accept file edits
22    AcceptEdits,
23    /// Plan mode - for planning tasks
24    Plan,
25    /// Allow all tools without prompting (use with caution)
26    BypassPermissions,
27}
28
29impl Default for PermissionMode {
30    fn default() -> Self {
31        Self::Default
32    }
33}
34
35/// Control protocol format for sending messages
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum ControlProtocolFormat {
38    /// Legacy format: {"type":"sdk_control_request","request":{...}}
39    Legacy,
40    /// New format: {"type":"control","control":{...}}
41    Control,
42    /// Auto-detect based on CLI capabilities (default to Legacy for compatibility)
43    Auto,
44}
45
46impl Default for ControlProtocolFormat {
47    fn default() -> Self {
48        // Default to Legacy for maximum compatibility
49        Self::Legacy
50    }
51}
52
53/// MCP (Model Context Protocol) server configuration
54#[derive(Clone)]
55pub enum McpServerConfig {
56    /// Standard I/O based MCP server
57    Stdio {
58        /// Command to execute
59        command: String,
60        /// Command arguments
61        args: Option<Vec<String>>,
62        /// Environment variables
63        env: Option<HashMap<String, String>>,
64    },
65    /// Server-Sent Events based MCP server
66    Sse {
67        /// Server URL
68        url: String,
69        /// HTTP headers
70        headers: Option<HashMap<String, String>>,
71    },
72    /// HTTP-based MCP server
73    Http {
74        /// Server URL
75        url: String,
76        /// HTTP headers
77        headers: Option<HashMap<String, String>>,
78    },
79    /// SDK MCP server (in-process)
80    Sdk {
81        /// Server name
82        name: String,
83        /// Server instance
84        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/// Permission update destination
202#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
203#[serde(rename_all = "camelCase")]
204pub enum PermissionUpdateDestination {
205    /// User settings
206    UserSettings,
207    /// Project settings
208    ProjectSettings,
209    /// Local settings
210    LocalSettings,
211    /// Session
212    Session,
213}
214
215/// Permission behavior
216#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
217#[serde(rename_all = "camelCase")]
218pub enum PermissionBehavior {
219    /// Allow the action
220    Allow,
221    /// Deny the action
222    Deny,
223    /// Ask the user
224    Ask,
225}
226
227/// Permission rule value
228#[derive(Debug, Clone, Serialize, Deserialize)]
229pub struct PermissionRuleValue {
230    /// Tool name
231    pub tool_name: String,
232    /// Rule content
233    pub rule_content: Option<String>,
234}
235
236/// Permission update type
237#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
238#[serde(rename_all = "camelCase")]
239pub enum PermissionUpdateType {
240    /// Add rules
241    AddRules,
242    /// Replace rules
243    ReplaceRules,
244    /// Remove rules
245    RemoveRules,
246    /// Set mode
247    SetMode,
248    /// Add directories
249    AddDirectories,
250    /// Remove directories
251    RemoveDirectories,
252}
253
254/// Permission update
255#[derive(Debug, Clone, Serialize, Deserialize)]
256#[serde(rename_all = "camelCase")]
257pub struct PermissionUpdate {
258    /// Update type
259    #[serde(rename = "type")]
260    pub update_type: PermissionUpdateType,
261    /// Rules to update
262    #[serde(skip_serializing_if = "Option::is_none")]
263    pub rules: Option<Vec<PermissionRuleValue>>,
264    /// Behavior to set
265    #[serde(skip_serializing_if = "Option::is_none")]
266    pub behavior: Option<PermissionBehavior>,
267    /// Mode to set
268    #[serde(skip_serializing_if = "Option::is_none")]
269    pub mode: Option<PermissionMode>,
270    /// Directories to add/remove
271    #[serde(skip_serializing_if = "Option::is_none")]
272    pub directories: Option<Vec<String>>,
273    /// Destination for the update
274    #[serde(skip_serializing_if = "Option::is_none")]
275    pub destination: Option<PermissionUpdateDestination>,
276}
277
278/// Tool permission context
279#[derive(Debug, Clone)]
280pub struct ToolPermissionContext {
281    /// Abort signal (future support)
282    pub signal: Option<Arc<dyn std::any::Any + Send + Sync>>,
283    /// Permission suggestions from CLI
284    pub suggestions: Vec<PermissionUpdate>,
285}
286
287/// Permission result - Allow
288#[derive(Debug, Clone)]
289pub struct PermissionResultAllow {
290    /// Updated input parameters
291    pub updated_input: Option<serde_json::Value>,
292    /// Updated permissions
293    pub updated_permissions: Option<Vec<PermissionUpdate>>,
294}
295
296/// Permission result - Deny
297#[derive(Debug, Clone)]
298pub struct PermissionResultDeny {
299    /// Denial message
300    pub message: String,
301    /// Whether to interrupt the conversation
302    pub interrupt: bool,
303}
304
305/// Permission result
306#[derive(Debug, Clone)]
307pub enum PermissionResult {
308    /// Allow the tool use
309    Allow(PermissionResultAllow),
310    /// Deny the tool use
311    Deny(PermissionResultDeny),
312}
313
314/// Tool permission callback trait
315#[async_trait]
316pub trait CanUseTool: Send + Sync {
317    /// Check if a tool can be used
318    async fn can_use_tool(
319        &self,
320        tool_name: &str,
321        input: &serde_json::Value,
322        context: &ToolPermissionContext,
323    ) -> PermissionResult;
324}
325
326/// Hook context
327#[derive(Debug, Clone)]
328pub struct HookContext {
329    /// Abort signal (future support)
330    pub signal: Option<Arc<dyn std::any::Any + Send + Sync>>,
331}
332
333/// Hook callback trait
334#[async_trait]
335pub trait HookCallback: Send + Sync {
336    /// Execute the hook
337    async fn execute(
338        &self,
339        input: &serde_json::Value,
340        tool_use_id: Option<&str>,
341        context: &HookContext,
342    ) -> serde_json::Value;
343}
344
345/// Hook matcher configuration
346#[derive(Clone)]
347pub struct HookMatcher {
348    /// Matcher criteria
349    pub matcher: Option<serde_json::Value>,
350    /// Callbacks to invoke
351    pub hooks: Vec<Arc<dyn HookCallback>>,
352}
353
354/// Setting source for configuration loading
355#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
356#[serde(rename_all = "lowercase")]
357pub enum SettingSource {
358    /// User-level settings
359    User,
360    /// Project-level settings
361    Project,
362    /// Local settings
363    Local,
364}
365
366/// Agent definition for programmatic agents
367#[derive(Debug, Clone, Serialize, Deserialize)]
368pub struct AgentDefinition {
369    /// Agent description
370    pub description: String,
371    /// Agent prompt
372    pub prompt: String,
373    /// Allowed tools for this agent
374    #[serde(skip_serializing_if = "Option::is_none")]
375    pub tools: Option<Vec<String>>,
376    /// Model to use
377    #[serde(skip_serializing_if = "Option::is_none")]
378    pub model: Option<String>,
379}
380
381/// System prompt configuration
382#[derive(Debug, Clone, Serialize, Deserialize)]
383#[serde(untagged)]
384pub enum SystemPrompt {
385    /// Simple string prompt
386    String(String),
387    /// Preset-based prompt with optional append
388    Preset {
389        #[serde(rename = "type")]
390        preset_type: String,  // "preset"
391        preset: String,       // e.g., "claude_code"
392        #[serde(skip_serializing_if = "Option::is_none")]
393        append: Option<String>,
394    },
395}
396
397/// Configuration options for Claude Code SDK
398#[derive(Clone, Default)]
399pub struct ClaudeCodeOptions {
400    /// System prompt configuration (simplified in v0.1.12+)
401    /// Can be either a string or a preset configuration
402    /// Replaces the old system_prompt and append_system_prompt fields
403    pub system_prompt_v2: Option<SystemPrompt>,
404    /// [DEPRECATED] System prompt to prepend to all messages
405    /// Use system_prompt_v2 instead
406    #[deprecated(since = "0.1.12", note = "Use system_prompt_v2 instead")]
407    pub system_prompt: Option<String>,
408    /// [DEPRECATED] Additional system prompt to append
409    /// Use system_prompt_v2 instead
410    #[deprecated(since = "0.1.12", note = "Use system_prompt_v2 instead")]
411    pub append_system_prompt: Option<String>,
412    /// List of allowed tools
413    pub allowed_tools: Vec<String>,
414    /// List of disallowed tools
415    pub disallowed_tools: Vec<String>,
416    /// Permission mode for tool execution
417    pub permission_mode: PermissionMode,
418    /// MCP server configurations
419    pub mcp_servers: HashMap<String, McpServerConfig>,
420    /// MCP tools to enable
421    pub mcp_tools: Vec<String>,
422    /// Maximum number of conversation turns
423    pub max_turns: Option<i32>,
424    /// Maximum thinking tokens
425    pub max_thinking_tokens: i32,
426    /// Maximum output tokens per response (1-32000, overrides CLAUDE_CODE_MAX_OUTPUT_TOKENS env var)
427    pub max_output_tokens: Option<u32>,
428    /// Model to use
429    pub model: Option<String>,
430    /// Working directory
431    pub cwd: Option<PathBuf>,
432    /// Continue from previous conversation
433    pub continue_conversation: bool,
434    /// Resume from a specific conversation ID
435    pub resume: Option<String>,
436    /// Custom permission prompt tool name
437    pub permission_prompt_tool_name: Option<String>,
438    /// Settings file path for Claude Code CLI
439    pub settings: Option<String>,
440    /// Additional directories to add as working directories
441    pub add_dirs: Vec<PathBuf>,
442    /// Extra arbitrary CLI flags
443    pub extra_args: HashMap<String, Option<String>>,
444    /// Environment variables to pass to the process
445    pub env: HashMap<String, String>,
446    /// Debug output stream (e.g., stderr)
447    pub debug_stderr: Option<Arc<Mutex<dyn Write + Send + Sync>>>,
448    /// Include partial assistant messages in streaming output
449    pub include_partial_messages: bool,
450    /// Tool permission callback
451    pub can_use_tool: Option<Arc<dyn CanUseTool>>,
452    /// Hook configurations
453    pub hooks: Option<HashMap<String, Vec<HookMatcher>>>,
454    /// Control protocol format (defaults to Legacy for compatibility)
455    pub control_protocol_format: ControlProtocolFormat,
456
457    // ========== Phase 2 Enhancements ==========
458    /// Setting sources to load (user, project, local)
459    /// When None, no filesystem settings are loaded (matches Python SDK v0.1.0 behavior)
460    pub setting_sources: Option<Vec<SettingSource>>,
461    /// Fork session when resuming instead of continuing
462    /// When true, creates a new branch from the resumed session
463    pub fork_session: bool,
464    /// Programmatic agent definitions
465    /// Define agents inline without filesystem dependencies
466    pub agents: Option<HashMap<String, AgentDefinition>>,
467}
468
469impl std::fmt::Debug for ClaudeCodeOptions {
470    #[allow(deprecated)]
471    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
472        f.debug_struct("ClaudeCodeOptions")
473            .field("system_prompt", &self.system_prompt)
474            .field("append_system_prompt", &self.append_system_prompt)
475            .field("allowed_tools", &self.allowed_tools)
476            .field("disallowed_tools", &self.disallowed_tools)
477            .field("permission_mode", &self.permission_mode)
478            .field("mcp_servers", &self.mcp_servers)
479            .field("mcp_tools", &self.mcp_tools)
480            .field("max_turns", &self.max_turns)
481            .field("max_thinking_tokens", &self.max_thinking_tokens)
482            .field("max_output_tokens", &self.max_output_tokens)
483            .field("model", &self.model)
484            .field("cwd", &self.cwd)
485            .field("continue_conversation", &self.continue_conversation)
486            .field("resume", &self.resume)
487            .field("permission_prompt_tool_name", &self.permission_prompt_tool_name)
488            .field("settings", &self.settings)
489            .field("add_dirs", &self.add_dirs)
490            .field("extra_args", &self.extra_args)
491            .field("env", &self.env)
492            .field("debug_stderr", &self.debug_stderr.is_some())
493            .field("include_partial_messages", &self.include_partial_messages)
494            .field("can_use_tool", &self.can_use_tool.is_some())
495            .field("hooks", &self.hooks.is_some())
496            .field("control_protocol_format", &self.control_protocol_format)
497            .finish()
498    }
499}
500
501impl ClaudeCodeOptions {
502    /// Create a new options builder
503    pub fn builder() -> ClaudeCodeOptionsBuilder {
504        ClaudeCodeOptionsBuilder::default()
505    }
506}
507
508/// Builder for ClaudeCodeOptions
509#[derive(Debug, Default)]
510pub struct ClaudeCodeOptionsBuilder {
511    options: ClaudeCodeOptions,
512}
513
514impl ClaudeCodeOptionsBuilder {
515    /// Set system prompt
516    #[allow(deprecated)]
517    pub fn system_prompt(mut self, prompt: impl Into<String>) -> Self {
518        self.options.system_prompt = Some(prompt.into());
519        self
520    }
521
522    /// Set append system prompt
523    #[allow(deprecated)]
524    pub fn append_system_prompt(mut self, prompt: impl Into<String>) -> Self {
525        self.options.append_system_prompt = Some(prompt.into());
526        self
527    }
528
529    /// Add allowed tools
530    pub fn allowed_tools(mut self, tools: Vec<String>) -> Self {
531        self.options.allowed_tools = tools;
532        self
533    }
534
535    /// Add a single allowed tool
536    pub fn allow_tool(mut self, tool: impl Into<String>) -> Self {
537        self.options.allowed_tools.push(tool.into());
538        self
539    }
540
541    /// Add disallowed tools
542    pub fn disallowed_tools(mut self, tools: Vec<String>) -> Self {
543        self.options.disallowed_tools = tools;
544        self
545    }
546
547    /// Add a single disallowed tool
548    pub fn disallow_tool(mut self, tool: impl Into<String>) -> Self {
549        self.options.disallowed_tools.push(tool.into());
550        self
551    }
552
553    /// Set permission mode
554    pub fn permission_mode(mut self, mode: PermissionMode) -> Self {
555        self.options.permission_mode = mode;
556        self
557    }
558
559    /// Add MCP server
560    pub fn add_mcp_server(mut self, name: impl Into<String>, config: McpServerConfig) -> Self {
561        self.options.mcp_servers.insert(name.into(), config);
562        self
563    }
564
565    /// Set all MCP servers from a map
566    pub fn mcp_servers(mut self, servers: HashMap<String, McpServerConfig>) -> Self {
567        self.options.mcp_servers = servers;
568        self
569    }
570
571    /// Set MCP tools
572    pub fn mcp_tools(mut self, tools: Vec<String>) -> Self {
573        self.options.mcp_tools = tools;
574        self
575    }
576
577    /// Set max turns
578    pub fn max_turns(mut self, turns: i32) -> Self {
579        self.options.max_turns = Some(turns);
580        self
581    }
582
583    /// Set max thinking tokens
584    pub fn max_thinking_tokens(mut self, tokens: i32) -> Self {
585        self.options.max_thinking_tokens = tokens;
586        self
587    }
588
589    /// Set max output tokens (1-32000, overrides CLAUDE_CODE_MAX_OUTPUT_TOKENS env var)
590    pub fn max_output_tokens(mut self, tokens: u32) -> Self {
591        self.options.max_output_tokens = Some(tokens.min(32000).max(1));
592        self
593    }
594
595    /// Set model
596    pub fn model(mut self, model: impl Into<String>) -> Self {
597        self.options.model = Some(model.into());
598        self
599    }
600
601    /// Set working directory
602    pub fn cwd(mut self, path: impl Into<PathBuf>) -> Self {
603        self.options.cwd = Some(path.into());
604        self
605    }
606
607    /// Enable continue conversation
608    pub fn continue_conversation(mut self, enable: bool) -> Self {
609        self.options.continue_conversation = enable;
610        self
611    }
612
613    /// Set resume conversation ID
614    pub fn resume(mut self, id: impl Into<String>) -> Self {
615        self.options.resume = Some(id.into());
616        self
617    }
618
619    /// Set permission prompt tool name
620    pub fn permission_prompt_tool_name(mut self, name: impl Into<String>) -> Self {
621        self.options.permission_prompt_tool_name = Some(name.into());
622        self
623    }
624
625    /// Set settings file path
626    pub fn settings(mut self, settings: impl Into<String>) -> Self {
627        self.options.settings = Some(settings.into());
628        self
629    }
630
631    /// Add directories as working directories
632    pub fn add_dirs(mut self, dirs: Vec<PathBuf>) -> Self {
633        self.options.add_dirs = dirs;
634        self
635    }
636
637    /// Add a single directory as working directory
638    pub fn add_dir(mut self, dir: impl Into<PathBuf>) -> Self {
639        self.options.add_dirs.push(dir.into());
640        self
641    }
642
643    /// Add extra CLI arguments
644    pub fn extra_args(mut self, args: HashMap<String, Option<String>>) -> Self {
645        self.options.extra_args = args;
646        self
647    }
648
649    /// Add a single extra CLI argument
650    pub fn add_extra_arg(mut self, key: impl Into<String>, value: Option<String>) -> Self {
651        self.options.extra_args.insert(key.into(), value);
652        self
653    }
654
655    /// Set control protocol format
656    pub fn control_protocol_format(mut self, format: ControlProtocolFormat) -> Self {
657        self.options.control_protocol_format = format;
658        self
659    }
660
661    /// Include partial assistant messages in streaming output
662    pub fn include_partial_messages(mut self, include: bool) -> Self {
663        self.options.include_partial_messages = include;
664        self
665    }
666
667    /// Enable fork_session behavior
668    pub fn fork_session(mut self, fork: bool) -> Self {
669        self.options.fork_session = fork;
670        self
671    }
672
673    /// Set setting sources
674    pub fn setting_sources(mut self, sources: Vec<SettingSource>) -> Self {
675        self.options.setting_sources = Some(sources);
676        self
677    }
678
679    /// Define programmatic agents
680    pub fn agents(mut self, agents: HashMap<String, AgentDefinition>) -> Self {
681        self.options.agents = Some(agents);
682        self
683    }
684
685    /// Build the options
686    pub fn build(self) -> ClaudeCodeOptions {
687        self.options
688    }
689}
690
691/// Main message type enum
692#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
693#[serde(tag = "type", rename_all = "lowercase")]
694pub enum Message {
695    /// User message
696    User {
697        /// Message content
698        message: UserMessage,
699    },
700    /// Assistant message
701    Assistant {
702        /// Message content
703        message: AssistantMessage,
704    },
705    /// System message
706    System {
707        /// Subtype of system message
708        subtype: String,
709        /// Additional data
710        data: serde_json::Value,
711    },
712    /// Result message indicating end of turn
713    Result {
714        /// Result subtype
715        subtype: String,
716        /// Duration in milliseconds
717        duration_ms: i64,
718        /// API duration in milliseconds
719        duration_api_ms: i64,
720        /// Whether an error occurred
721        is_error: bool,
722        /// Number of turns
723        num_turns: i32,
724        /// Session ID
725        session_id: String,
726        /// Total cost in USD
727        #[serde(skip_serializing_if = "Option::is_none")]
728        total_cost_usd: Option<f64>,
729        /// Usage statistics
730        #[serde(skip_serializing_if = "Option::is_none")]
731        usage: Option<serde_json::Value>,
732        /// Result message
733        #[serde(skip_serializing_if = "Option::is_none")]
734        result: Option<String>,
735    },
736}
737
738/// User message content
739#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
740pub struct UserMessage {
741    /// Message content
742    pub content: String,
743}
744
745/// Assistant message content
746#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
747pub struct AssistantMessage {
748    /// Content blocks
749    pub content: Vec<ContentBlock>,
750}
751
752/// Result message (re-export for convenience)  
753pub use Message::Result as ResultMessage;
754/// System message (re-export for convenience)
755pub use Message::System as SystemMessage;
756
757/// Content block types
758#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
759#[serde(untagged)]
760pub enum ContentBlock {
761    /// Text content
762    Text(TextContent),
763    /// Thinking content
764    Thinking(ThinkingContent),
765    /// Tool use request
766    ToolUse(ToolUseContent),
767    /// Tool result
768    ToolResult(ToolResultContent),
769}
770
771/// Text content block
772#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
773pub struct TextContent {
774    /// Text content
775    pub text: String,
776}
777
778/// Thinking content block
779#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
780pub struct ThinkingContent {
781    /// Thinking content
782    pub thinking: String,
783    /// Signature
784    pub signature: String,
785}
786
787/// Tool use content block
788#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
789pub struct ToolUseContent {
790    /// Tool use ID
791    pub id: String,
792    /// Tool name
793    pub name: String,
794    /// Tool input parameters
795    pub input: serde_json::Value,
796}
797
798/// Tool result content block
799#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
800pub struct ToolResultContent {
801    /// Tool use ID this result corresponds to
802    pub tool_use_id: String,
803    /// Result content
804    #[serde(skip_serializing_if = "Option::is_none")]
805    pub content: Option<ContentValue>,
806    /// Whether this is an error result
807    #[serde(skip_serializing_if = "Option::is_none")]
808    pub is_error: Option<bool>,
809}
810
811/// Content value for tool results
812#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
813#[serde(untagged)]
814pub enum ContentValue {
815    /// Text content
816    Text(String),
817    /// Structured content
818    Structured(Vec<serde_json::Value>),
819}
820
821/// User content structure for internal use
822#[derive(Debug, Clone, Serialize, Deserialize)]
823pub struct UserContent {
824    /// Role (always "user")
825    pub role: String,
826    /// Message content
827    pub content: String,
828}
829
830/// Assistant content structure for internal use
831#[derive(Debug, Clone, Serialize, Deserialize)]
832pub struct AssistantContent {
833    /// Role (always "assistant")
834    pub role: String,
835    /// Content blocks
836    pub content: Vec<ContentBlock>,
837}
838
839/// SDK Control Protocol - Interrupt request
840#[derive(Debug, Clone, Serialize, Deserialize)]
841pub struct SDKControlInterruptRequest {
842    /// Subtype
843    pub subtype: String,  // "interrupt"
844}
845
846/// SDK Control Protocol - Permission request
847#[derive(Debug, Clone, Serialize, Deserialize)]
848#[serde(rename_all = "camelCase")]
849pub struct SDKControlPermissionRequest {
850    /// Subtype
851    pub subtype: String,  // "can_use_tool"
852    /// Tool name
853    pub tool_name: String,
854    /// Tool input
855    pub input: serde_json::Value,
856    /// Permission suggestions
857    #[serde(skip_serializing_if = "Option::is_none")]
858    pub permission_suggestions: Option<Vec<PermissionUpdate>>,
859    /// Blocked path
860    #[serde(skip_serializing_if = "Option::is_none")]
861    pub blocked_path: Option<String>,
862}
863
864/// SDK Control Protocol - Initialize request
865#[derive(Debug, Clone, Serialize, Deserialize)]
866pub struct SDKControlInitializeRequest {
867    /// Subtype
868    pub subtype: String,  // "initialize"
869    /// Hooks configuration
870    #[serde(skip_serializing_if = "Option::is_none")]
871    pub hooks: Option<HashMap<String, serde_json::Value>>,
872}
873
874/// SDK Control Protocol - Set permission mode request
875#[derive(Debug, Clone, Serialize, Deserialize)]
876#[serde(rename_all = "camelCase")]
877pub struct SDKControlSetPermissionModeRequest {
878    /// Subtype
879    pub subtype: String,  // "set_permission_mode"
880    /// Permission mode
881    pub mode: String,
882}
883
884/// SDK Control Protocol - Set model request
885#[derive(Debug, Clone, Serialize, Deserialize)]
886#[serde(rename_all = "camelCase")]
887pub struct SDKControlSetModelRequest {
888    /// Subtype
889    pub subtype: String, // "set_model"
890    /// Model to set (None to clear)
891    #[serde(skip_serializing_if = "Option::is_none")]
892    pub model: Option<String>,
893}
894
895/// SDK Hook callback request
896#[derive(Debug, Clone, Serialize, Deserialize)]
897#[serde(rename_all = "camelCase")]
898pub struct SDKHookCallbackRequest {
899    /// Subtype
900    pub subtype: String,  // "hook_callback"
901    /// Callback ID
902    pub callback_id: String,
903    /// Input data
904    pub input: serde_json::Value,
905    /// Tool use ID
906    #[serde(skip_serializing_if = "Option::is_none")]
907    pub tool_use_id: Option<String>,
908}
909
910/// SDK Control Protocol - MCP message request
911#[derive(Debug, Clone, Serialize, Deserialize)]
912#[serde(rename_all = "camelCase")]
913pub struct SDKControlMcpMessageRequest {
914    /// Subtype
915    pub subtype: String,  // "mcp_message"
916    /// MCP server name
917    pub mcp_server_name: String,
918    /// Message to send
919    pub message: serde_json::Value,
920}
921
922/// SDK Control Protocol request types
923#[derive(Debug, Clone, Serialize, Deserialize)]
924#[serde(tag = "type", rename_all = "snake_case")]
925pub enum SDKControlRequest {
926    /// Interrupt request
927    #[serde(rename = "interrupt")]
928    Interrupt(SDKControlInterruptRequest),
929    /// Permission request
930    #[serde(rename = "can_use_tool")]
931    CanUseTool(SDKControlPermissionRequest),
932    /// Initialize request
933    #[serde(rename = "initialize")]
934    Initialize(SDKControlInitializeRequest),
935    /// Set permission mode
936    #[serde(rename = "set_permission_mode")]
937    SetPermissionMode(SDKControlSetPermissionModeRequest),
938    /// Set model
939    #[serde(rename = "set_model")]
940    SetModel(SDKControlSetModelRequest),
941    /// Hook callback
942    #[serde(rename = "hook_callback")]
943    HookCallback(SDKHookCallbackRequest),
944    /// MCP message
945    #[serde(rename = "mcp_message")]
946    McpMessage(SDKControlMcpMessageRequest),
947}
948
949/// Control request types (legacy, keeping for compatibility)
950#[derive(Debug, Clone, Serialize, Deserialize)]
951#[serde(tag = "type", rename_all = "lowercase")]
952pub enum ControlRequest {
953    /// Interrupt the current operation
954    Interrupt {
955        /// Request ID
956        request_id: String,
957    },
958}
959
960/// Control response types (legacy, keeping for compatibility)
961#[derive(Debug, Clone, Serialize, Deserialize)]
962#[serde(tag = "type", rename_all = "lowercase")]
963pub enum ControlResponse {
964    /// Interrupt acknowledged
965    InterruptAck {
966        /// Request ID
967        request_id: String,
968        /// Whether interrupt was successful
969        success: bool,
970    },
971}
972
973#[cfg(test)]
974mod tests {
975    use super::*;
976
977    #[test]
978    fn test_permission_mode_serialization() {
979        let mode = PermissionMode::AcceptEdits;
980        let json = serde_json::to_string(&mode).unwrap();
981        assert_eq!(json, r#""acceptEdits""#);
982
983        let deserialized: PermissionMode = serde_json::from_str(&json).unwrap();
984        assert_eq!(deserialized, mode);
985
986        // Test Plan mode
987        let plan_mode = PermissionMode::Plan;
988        let plan_json = serde_json::to_string(&plan_mode).unwrap();
989        assert_eq!(plan_json, r#""plan""#);
990
991        let plan_deserialized: PermissionMode = serde_json::from_str(&plan_json).unwrap();
992        assert_eq!(plan_deserialized, plan_mode);
993    }
994
995    #[test]
996    fn test_message_serialization() {
997        let msg = Message::User {
998            message: UserMessage {
999                content: "Hello".to_string(),
1000            },
1001        };
1002
1003        let json = serde_json::to_string(&msg).unwrap();
1004        assert!(json.contains(r#""type":"user""#));
1005        assert!(json.contains(r#""content":"Hello""#));
1006
1007        let deserialized: Message = serde_json::from_str(&json).unwrap();
1008        assert_eq!(deserialized, msg);
1009    }
1010
1011    #[test]
1012    fn test_options_builder() {
1013        let options = ClaudeCodeOptions::builder()
1014            .system_prompt("Test prompt")
1015            .model("claude-3-opus")
1016            .permission_mode(PermissionMode::AcceptEdits)
1017            .allow_tool("read")
1018            .allow_tool("write")
1019            .max_turns(10)
1020            .build();
1021
1022        assert_eq!(options.system_prompt, Some("Test prompt".to_string()));
1023        assert_eq!(options.model, Some("claude-3-opus".to_string()));
1024        assert_eq!(options.permission_mode, PermissionMode::AcceptEdits);
1025        assert_eq!(options.allowed_tools, vec!["read", "write"]);
1026        assert_eq!(options.max_turns, Some(10));
1027    }
1028
1029    #[test]
1030    fn test_extra_args() {
1031        let mut extra_args = HashMap::new();
1032        extra_args.insert("custom-flag".to_string(), Some("value".to_string()));
1033        extra_args.insert("boolean-flag".to_string(), None);
1034
1035        let options = ClaudeCodeOptions::builder()
1036            .extra_args(extra_args.clone())
1037            .add_extra_arg("another-flag", Some("another-value".to_string()))
1038            .build();
1039
1040        assert_eq!(options.extra_args.len(), 3);
1041        assert_eq!(options.extra_args.get("custom-flag"), Some(&Some("value".to_string())));
1042        assert_eq!(options.extra_args.get("boolean-flag"), Some(&None));
1043        assert_eq!(options.extra_args.get("another-flag"), Some(&Some("another-value".to_string())));
1044    }
1045
1046    #[test]
1047    fn test_thinking_content_serialization() {
1048        let thinking = ThinkingContent {
1049            thinking: "Let me think about this...".to_string(),
1050            signature: "sig123".to_string(),
1051        };
1052
1053        let json = serde_json::to_string(&thinking).unwrap();
1054        assert!(json.contains(r#""thinking":"Let me think about this...""#));
1055        assert!(json.contains(r#""signature":"sig123""#));
1056
1057        let deserialized: ThinkingContent = serde_json::from_str(&json).unwrap();
1058        assert_eq!(deserialized.thinking, thinking.thinking);
1059        assert_eq!(deserialized.signature, thinking.signature);
1060    }
1061}