Skip to main content

claude_agents_sdk/
types.rs

1//! Type definitions for the Claude Agents SDK.
2//!
3//! This module contains all the type definitions used throughout the SDK,
4//! including messages, content blocks, options, hooks, and control protocol types.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::future::Future;
9use std::path::PathBuf;
10use std::pin::Pin;
11use std::sync::Arc;
12
13// ============================================================================
14// Permission Types
15// ============================================================================
16
17/// Permission modes controlling how the CLI handles tool permissions.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
19#[serde(rename_all = "camelCase")]
20pub enum PermissionMode {
21    /// Default permission mode - asks for permission on sensitive operations.
22    #[default]
23    #[serde(rename = "default")]
24    Default,
25    /// Automatically accept all file edits.
26    #[serde(rename = "acceptEdits")]
27    AcceptEdits,
28    /// Plan mode - only plans actions without executing.
29    #[serde(rename = "plan")]
30    Plan,
31    /// Bypass all permission checks (dangerous).
32    #[serde(rename = "bypassPermissions")]
33    BypassPermissions,
34}
35
36/// Permission behavior for tool use.
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
38#[serde(rename_all = "lowercase")]
39pub enum PermissionBehavior {
40    /// Allow the tool to execute.
41    Allow,
42    /// Deny the tool execution.
43    Deny,
44    /// Ask for permission.
45    Ask,
46}
47
48/// Destination for permission updates.
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
50#[serde(rename_all = "camelCase")]
51pub enum PermissionUpdateDestination {
52    /// Store in user settings.
53    UserSettings,
54    /// Store in project settings.
55    ProjectSettings,
56    /// Store in local settings.
57    LocalSettings,
58    /// Store in current session only.
59    Session,
60}
61
62/// Permission rule value.
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct PermissionRuleValue {
65    /// The tool name this rule applies to.
66    #[serde(rename = "toolName")]
67    pub tool_name: String,
68    /// Optional rule content.
69    #[serde(rename = "ruleContent", skip_serializing_if = "Option::is_none")]
70    pub rule_content: Option<String>,
71}
72
73/// Type of permission update.
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
75#[serde(rename_all = "camelCase")]
76pub enum PermissionUpdateType {
77    /// Add new permission rules.
78    AddRules,
79    /// Replace existing permission rules.
80    ReplaceRules,
81    /// Remove permission rules.
82    RemoveRules,
83    /// Set the permission mode.
84    SetMode,
85    /// Add directories to permissions.
86    AddDirectories,
87    /// Remove directories from permissions.
88    RemoveDirectories,
89}
90
91/// Permission update configuration.
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct PermissionUpdate {
94    /// Type of update to perform.
95    #[serde(rename = "type")]
96    pub update_type: PermissionUpdateType,
97    /// Rules to add/replace/remove.
98    #[serde(skip_serializing_if = "Option::is_none")]
99    pub rules: Option<Vec<PermissionRuleValue>>,
100    /// Behavior for the rules.
101    #[serde(skip_serializing_if = "Option::is_none")]
102    pub behavior: Option<PermissionBehavior>,
103    /// Mode to set.
104    #[serde(skip_serializing_if = "Option::is_none")]
105    pub mode: Option<PermissionMode>,
106    /// Directories to add/remove.
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub directories: Option<Vec<String>>,
109    /// Where to store the update.
110    #[serde(skip_serializing_if = "Option::is_none")]
111    pub destination: Option<PermissionUpdateDestination>,
112}
113
114// ============================================================================
115// Tool Permission Callback Types
116// ============================================================================
117
118/// Context information for tool permission callbacks.
119#[derive(Debug, Clone, Default)]
120pub struct ToolPermissionContext {
121    /// Permission suggestions from CLI.
122    pub suggestions: Vec<PermissionUpdate>,
123}
124
125/// Allow permission result.
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct PermissionResultAllow {
128    /// Always "allow".
129    pub behavior: String,
130    /// Updated input for the tool.
131    #[serde(rename = "updatedInput", skip_serializing_if = "Option::is_none")]
132    pub updated_input: Option<serde_json::Value>,
133    /// Permission updates to apply.
134    #[serde(rename = "updatedPermissions", skip_serializing_if = "Option::is_none")]
135    pub updated_permissions: Option<Vec<PermissionUpdate>>,
136}
137
138impl PermissionResultAllow {
139    /// Create a new allow result.
140    pub fn new() -> Self {
141        Self {
142            behavior: "allow".to_string(),
143            updated_input: None,
144            updated_permissions: None,
145        }
146    }
147
148    /// Create an allow result with updated input.
149    pub fn with_updated_input(input: serde_json::Value) -> Self {
150        Self {
151            behavior: "allow".to_string(),
152            updated_input: Some(input),
153            updated_permissions: None,
154        }
155    }
156}
157
158impl Default for PermissionResultAllow {
159    fn default() -> Self {
160        Self::new()
161    }
162}
163
164/// Deny permission result.
165#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct PermissionResultDeny {
167    /// Always "deny".
168    pub behavior: String,
169    /// Message explaining why permission was denied.
170    #[serde(default)]
171    pub message: String,
172    /// Whether to interrupt execution.
173    #[serde(default)]
174    pub interrupt: bool,
175}
176
177impl PermissionResultDeny {
178    /// Create a new deny result.
179    pub fn new() -> Self {
180        Self {
181            behavior: "deny".to_string(),
182            message: String::new(),
183            interrupt: false,
184        }
185    }
186
187    /// Create a deny result with a message.
188    pub fn with_message(message: impl Into<String>) -> Self {
189        Self {
190            behavior: "deny".to_string(),
191            message: message.into(),
192            interrupt: false,
193        }
194    }
195
196    /// Create a deny result that also interrupts.
197    pub fn with_interrupt(message: impl Into<String>) -> Self {
198        Self {
199            behavior: "deny".to_string(),
200            message: message.into(),
201            interrupt: true,
202        }
203    }
204}
205
206impl Default for PermissionResultDeny {
207    fn default() -> Self {
208        Self::new()
209    }
210}
211
212/// Permission result from a tool permission callback.
213#[derive(Debug, Clone, Serialize, Deserialize)]
214#[serde(untagged)]
215pub enum PermissionResult {
216    /// Allow the tool to execute.
217    Allow(PermissionResultAllow),
218    /// Deny the tool execution.
219    Deny(PermissionResultDeny),
220}
221
222impl PermissionResult {
223    /// Create an allow result.
224    pub fn allow() -> Self {
225        Self::Allow(PermissionResultAllow::new())
226    }
227
228    /// Create a deny result.
229    pub fn deny() -> Self {
230        Self::Deny(PermissionResultDeny::new())
231    }
232
233    /// Create a deny result with a message.
234    pub fn deny_with_message(message: impl Into<String>) -> Self {
235        Self::Deny(PermissionResultDeny::with_message(message))
236    }
237}
238
239/// The async future type returned by tool permission callbacks.
240pub type CanUseToolFuture = Pin<Box<dyn Future<Output = PermissionResult> + Send>>;
241
242/// Type alias for the tool permission callback function.
243///
244/// This callback is invoked when Claude wants to use a tool, allowing you to
245/// approve, deny, or modify the tool execution.
246///
247/// # Arguments
248/// * `tool_name` - Name of the tool being requested (e.g., "Bash", "Read", "Write")
249/// * `input` - The tool input as JSON (tool-specific parameters)
250/// * `context` - Additional context including permission suggestions
251///
252/// # Returns
253/// A [`PermissionResult`] indicating whether to allow or deny the tool use.
254///
255/// # Example
256/// ```ignore
257/// let callback: CanUseTool = Arc::new(|tool_name, input, context| {
258///     Box::pin(async move {
259///         if tool_name == "Bash" {
260///             PermissionResult::deny_with_message("Bash is disabled")
261///         } else {
262///             PermissionResult::allow()
263///         }
264///     })
265/// });
266/// ```
267pub type CanUseTool =
268    Arc<dyn Fn(String, serde_json::Value, ToolPermissionContext) -> CanUseToolFuture + Send + Sync>;
269
270// ============================================================================
271// Hook Types
272// ============================================================================
273
274/// Hook event types.
275#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
276pub enum HookEvent {
277    /// Before a tool is used.
278    PreToolUse,
279    /// After a tool is used.
280    PostToolUse,
281    /// After a tool use fails.
282    PostToolUseFailure,
283    /// When user submits a prompt.
284    UserPromptSubmit,
285    /// Stop hook.
286    Stop,
287    /// Subagent stop hook.
288    SubagentStop,
289    /// Before context compaction.
290    PreCompact,
291    /// Notification hook.
292    Notification,
293    /// Subagent start hook.
294    SubagentStart,
295    /// Permission request hook.
296    PermissionRequest,
297}
298
299/// Base hook input fields.
300#[derive(Debug, Clone, Serialize, Deserialize)]
301pub struct BaseHookInput {
302    /// Session ID.
303    pub session_id: String,
304    /// Path to transcript file.
305    pub transcript_path: String,
306    /// Current working directory.
307    pub cwd: String,
308    /// Current permission mode.
309    #[serde(skip_serializing_if = "Option::is_none")]
310    pub permission_mode: Option<String>,
311}
312
313/// Input for PreToolUse hook events.
314#[derive(Debug, Clone, Serialize, Deserialize)]
315pub struct PreToolUseHookInput {
316    /// Base fields.
317    #[serde(flatten)]
318    pub base: BaseHookInput,
319    /// Hook event name.
320    #[serde(default)]
321    pub hook_event_name: String,
322    /// Name of the tool being used.
323    pub tool_name: String,
324    /// Input to the tool.
325    pub tool_input: serde_json::Value,
326    /// Tool use ID.
327    #[serde(default)]
328    pub tool_use_id: String,
329}
330
331/// Input for PostToolUse hook events.
332#[derive(Debug, Clone, Serialize, Deserialize)]
333pub struct PostToolUseHookInput {
334    /// Base fields.
335    #[serde(flatten)]
336    pub base: BaseHookInput,
337    /// Hook event name.
338    #[serde(default)]
339    pub hook_event_name: String,
340    /// Name of the tool that was used.
341    pub tool_name: String,
342    /// Input that was passed to the tool.
343    pub tool_input: serde_json::Value,
344    /// Response from the tool.
345    pub tool_response: serde_json::Value,
346    /// Tool use ID.
347    #[serde(default)]
348    pub tool_use_id: String,
349}
350
351/// Input for PostToolUseFailure hook events.
352#[derive(Debug, Clone, Serialize, Deserialize)]
353pub struct PostToolUseFailureHookInput {
354    /// Base fields.
355    #[serde(flatten)]
356    pub base: BaseHookInput,
357    /// Hook event name.
358    #[serde(default)]
359    pub hook_event_name: String,
360    /// Name of the tool that failed.
361    pub tool_name: String,
362    /// Input that was passed to the tool.
363    pub tool_input: serde_json::Value,
364    /// Tool use ID.
365    pub tool_use_id: String,
366    /// Error message.
367    pub error: String,
368    /// Whether the failure was due to an interrupt.
369    #[serde(skip_serializing_if = "Option::is_none")]
370    pub is_interrupt: Option<bool>,
371}
372
373/// Input for UserPromptSubmit hook events.
374#[derive(Debug, Clone, Serialize, Deserialize)]
375pub struct UserPromptSubmitHookInput {
376    /// Base fields.
377    #[serde(flatten)]
378    pub base: BaseHookInput,
379    /// Hook event name.
380    #[serde(default)]
381    pub hook_event_name: String,
382    /// The submitted prompt.
383    pub prompt: String,
384}
385
386/// Input for Stop hook events.
387#[derive(Debug, Clone, Serialize, Deserialize)]
388pub struct StopHookInput {
389    /// Base fields.
390    #[serde(flatten)]
391    pub base: BaseHookInput,
392    /// Hook event name.
393    #[serde(default)]
394    pub hook_event_name: String,
395    /// Whether stop hook is active.
396    pub stop_hook_active: bool,
397}
398
399/// Input for SubagentStop hook events.
400#[derive(Debug, Clone, Serialize, Deserialize)]
401pub struct SubagentStopHookInput {
402    /// Base fields.
403    #[serde(flatten)]
404    pub base: BaseHookInput,
405    /// Hook event name.
406    #[serde(default)]
407    pub hook_event_name: String,
408    /// Whether stop hook is active.
409    pub stop_hook_active: bool,
410    /// Agent ID.
411    #[serde(default)]
412    pub agent_id: String,
413    /// Path to agent transcript.
414    #[serde(default)]
415    pub agent_transcript_path: String,
416    /// Agent type.
417    #[serde(default)]
418    pub agent_type: String,
419}
420
421/// Trigger for PreCompact hook.
422#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
423#[serde(rename_all = "lowercase")]
424pub enum CompactTrigger {
425    /// Manual compaction.
426    Manual,
427    /// Automatic compaction.
428    Auto,
429}
430
431/// Input for PreCompact hook events.
432#[derive(Debug, Clone, Serialize, Deserialize)]
433pub struct PreCompactHookInput {
434    /// Base fields.
435    #[serde(flatten)]
436    pub base: BaseHookInput,
437    /// Hook event name.
438    #[serde(default)]
439    pub hook_event_name: String,
440    /// What triggered the compaction.
441    pub trigger: CompactTrigger,
442    /// Custom instructions for compaction.
443    #[serde(skip_serializing_if = "Option::is_none")]
444    pub custom_instructions: Option<String>,
445}
446
447/// Input for Notification hook events.
448#[derive(Debug, Clone, Serialize, Deserialize)]
449pub struct NotificationHookInput {
450    /// Base fields.
451    #[serde(flatten)]
452    pub base: BaseHookInput,
453    /// Hook event name.
454    #[serde(default)]
455    pub hook_event_name: String,
456    /// Notification message.
457    pub message: String,
458    /// Notification title.
459    #[serde(skip_serializing_if = "Option::is_none")]
460    pub title: Option<String>,
461    /// Notification type.
462    pub notification_type: String,
463}
464
465/// Input for SubagentStart hook events.
466#[derive(Debug, Clone, Serialize, Deserialize)]
467pub struct SubagentStartHookInput {
468    /// Base fields.
469    #[serde(flatten)]
470    pub base: BaseHookInput,
471    /// Hook event name.
472    #[serde(default)]
473    pub hook_event_name: String,
474    /// Agent ID.
475    pub agent_id: String,
476    /// Agent type.
477    pub agent_type: String,
478}
479
480/// Input for PermissionRequest hook events.
481#[derive(Debug, Clone, Serialize, Deserialize)]
482pub struct PermissionRequestHookInput {
483    /// Base fields.
484    #[serde(flatten)]
485    pub base: BaseHookInput,
486    /// Hook event name.
487    #[serde(default)]
488    pub hook_event_name: String,
489    /// Tool name requesting permission.
490    pub tool_name: String,
491    /// Tool input.
492    pub tool_input: serde_json::Value,
493    /// Permission suggestions.
494    #[serde(skip_serializing_if = "Option::is_none")]
495    pub permission_suggestions: Option<Vec<serde_json::Value>>,
496}
497
498/// Union of all hook input types.
499#[derive(Debug, Clone, Serialize, Deserialize)]
500#[serde(tag = "hook_event_name")]
501pub enum HookInput {
502    /// PreToolUse hook input.
503    PreToolUse(PreToolUseHookInput),
504    /// PostToolUse hook input.
505    PostToolUse(PostToolUseHookInput),
506    /// PostToolUseFailure hook input.
507    PostToolUseFailure(PostToolUseFailureHookInput),
508    /// UserPromptSubmit hook input.
509    UserPromptSubmit(UserPromptSubmitHookInput),
510    /// Stop hook input.
511    Stop(StopHookInput),
512    /// SubagentStop hook input.
513    SubagentStop(SubagentStopHookInput),
514    /// PreCompact hook input.
515    PreCompact(PreCompactHookInput),
516    /// Notification hook input.
517    Notification(NotificationHookInput),
518    /// SubagentStart hook input.
519    SubagentStart(SubagentStartHookInput),
520    /// PermissionRequest hook input.
521    PermissionRequest(PermissionRequestHookInput),
522}
523
524/// Hook-specific output for PreToolUse events.
525#[derive(Debug, Clone, Default, Serialize, Deserialize)]
526#[serde(rename_all = "camelCase")]
527pub struct PreToolUseHookSpecificOutput {
528    /// Event name.
529    #[serde(rename = "hookEventName")]
530    pub hook_event_name: String,
531    /// Permission decision.
532    #[serde(skip_serializing_if = "Option::is_none")]
533    pub permission_decision: Option<PermissionBehavior>,
534    /// Reason for decision.
535    #[serde(skip_serializing_if = "Option::is_none")]
536    pub permission_decision_reason: Option<String>,
537    /// Updated input.
538    #[serde(skip_serializing_if = "Option::is_none")]
539    pub updated_input: Option<serde_json::Value>,
540    /// Additional context to add.
541    #[serde(skip_serializing_if = "Option::is_none")]
542    pub additional_context: Option<String>,
543}
544
545/// Hook-specific output for PostToolUse events.
546#[derive(Debug, Clone, Default, Serialize, Deserialize)]
547#[serde(rename_all = "camelCase")]
548pub struct PostToolUseHookSpecificOutput {
549    /// Event name.
550    #[serde(rename = "hookEventName")]
551    pub hook_event_name: String,
552    /// Additional context to add.
553    #[serde(skip_serializing_if = "Option::is_none")]
554    pub additional_context: Option<String>,
555    /// Updated MCP tool output.
556    #[serde(skip_serializing_if = "Option::is_none")]
557    pub updated_mcp_tool_output: Option<serde_json::Value>,
558}
559
560/// Hook-specific output for PostToolUseFailure events.
561#[derive(Debug, Clone, Default, Serialize, Deserialize)]
562#[serde(rename_all = "camelCase")]
563pub struct PostToolUseFailureHookSpecificOutput {
564    /// Event name.
565    #[serde(rename = "hookEventName")]
566    pub hook_event_name: String,
567    /// Additional context to add.
568    #[serde(skip_serializing_if = "Option::is_none")]
569    pub additional_context: Option<String>,
570}
571
572/// Hook-specific output for UserPromptSubmit events.
573#[derive(Debug, Clone, Default, Serialize, Deserialize)]
574#[serde(rename_all = "camelCase")]
575pub struct UserPromptSubmitHookSpecificOutput {
576    /// Event name.
577    #[serde(rename = "hookEventName")]
578    pub hook_event_name: String,
579    /// Additional context to add.
580    #[serde(skip_serializing_if = "Option::is_none")]
581    pub additional_context: Option<String>,
582}
583
584/// Hook-specific output for Notification events.
585#[derive(Debug, Clone, Default, Serialize, Deserialize)]
586#[serde(rename_all = "camelCase")]
587pub struct NotificationHookSpecificOutput {
588    /// Event name.
589    #[serde(rename = "hookEventName")]
590    pub hook_event_name: String,
591    /// Additional context to add.
592    #[serde(skip_serializing_if = "Option::is_none")]
593    pub additional_context: Option<String>,
594}
595
596/// Hook-specific output for SubagentStart events.
597#[derive(Debug, Clone, Default, Serialize, Deserialize)]
598#[serde(rename_all = "camelCase")]
599pub struct SubagentStartHookSpecificOutput {
600    /// Event name.
601    #[serde(rename = "hookEventName")]
602    pub hook_event_name: String,
603    /// Additional context to add.
604    #[serde(skip_serializing_if = "Option::is_none")]
605    pub additional_context: Option<String>,
606}
607
608/// Hook-specific output for PermissionRequest events.
609#[derive(Debug, Clone, Default, Serialize, Deserialize)]
610#[serde(rename_all = "camelCase")]
611pub struct PermissionRequestHookSpecificOutput {
612    /// Event name.
613    #[serde(rename = "hookEventName")]
614    pub hook_event_name: String,
615    /// Decision for the permission request.
616    pub decision: serde_json::Value,
617}
618
619/// Union of hook-specific outputs.
620#[derive(Debug, Clone, Serialize, Deserialize)]
621#[serde(untagged)]
622pub enum HookSpecificOutput {
623    /// PreToolUse specific output.
624    PreToolUse(PreToolUseHookSpecificOutput),
625    /// PostToolUse specific output.
626    PostToolUse(PostToolUseHookSpecificOutput),
627    /// PostToolUseFailure specific output.
628    PostToolUseFailure(PostToolUseFailureHookSpecificOutput),
629    /// UserPromptSubmit specific output.
630    UserPromptSubmit(UserPromptSubmitHookSpecificOutput),
631    /// Notification specific output.
632    Notification(NotificationHookSpecificOutput),
633    /// SubagentStart specific output.
634    SubagentStart(SubagentStartHookSpecificOutput),
635    /// PermissionRequest specific output.
636    PermissionRequest(PermissionRequestHookSpecificOutput),
637}
638
639/// Async hook output that defers execution.
640#[derive(Debug, Clone, Serialize, Deserialize)]
641pub struct AsyncHookOutput {
642    /// Set to true to defer execution.
643    #[serde(rename = "async")]
644    pub async_: bool,
645    /// Optional timeout in milliseconds.
646    #[serde(rename = "asyncTimeout", skip_serializing_if = "Option::is_none")]
647    pub async_timeout: Option<u64>,
648}
649
650/// Synchronous hook output with control fields.
651#[derive(Debug, Clone, Default, Serialize, Deserialize)]
652#[serde(rename_all = "camelCase")]
653pub struct SyncHookOutput {
654    /// Whether to continue execution.
655    #[serde(rename = "continue", skip_serializing_if = "Option::is_none")]
656    pub continue_: Option<bool>,
657    /// Whether to suppress output.
658    #[serde(skip_serializing_if = "Option::is_none")]
659    pub suppress_output: Option<bool>,
660    /// Reason for stopping.
661    #[serde(skip_serializing_if = "Option::is_none")]
662    pub stop_reason: Option<String>,
663    /// Decision (e.g., "block").
664    #[serde(skip_serializing_if = "Option::is_none")]
665    pub decision: Option<String>,
666    /// System message to display.
667    #[serde(skip_serializing_if = "Option::is_none")]
668    pub system_message: Option<String>,
669    /// Reason for decision.
670    #[serde(skip_serializing_if = "Option::is_none")]
671    pub reason: Option<String>,
672    /// Hook-specific output.
673    #[serde(skip_serializing_if = "Option::is_none")]
674    pub hook_specific_output: Option<HookSpecificOutput>,
675}
676
677/// Hook output type.
678#[derive(Debug, Clone, Serialize, Deserialize)]
679#[serde(untagged)]
680pub enum HookOutput {
681    /// Async output.
682    Async(AsyncHookOutput),
683    /// Sync output.
684    Sync(SyncHookOutput),
685}
686
687impl Default for HookOutput {
688    fn default() -> Self {
689        Self::Sync(SyncHookOutput::default())
690    }
691}
692
693/// Context for hook callbacks.
694#[derive(Debug, Clone, Default)]
695pub struct HookContext {
696    // Reserved for future abort signal support.
697}
698
699/// The async future type returned by hook callbacks.
700pub type HookCallbackFuture = Pin<Box<dyn Future<Output = HookOutput> + Send>>;
701
702/// Type alias for hook callback functions.
703///
704/// Hooks are invoked at specific points during Claude's execution, allowing you
705/// to observe or modify behavior.
706///
707/// # Arguments
708/// * `input` - The hook input containing event-specific data
709/// * `tool_use_id` - Optional tool use ID (for pre/post tool hooks)
710/// * `context` - Hook context (reserved for future use)
711///
712/// # Returns
713/// A [`HookOutput`] that can modify the tool input, block execution, or log a message.
714///
715/// # Example
716/// ```ignore
717/// let callback: HookCallback = Arc::new(|input, tool_use_id, context| {
718///     Box::pin(async move {
719///         println!("Tool called: {:?}", tool_use_id);
720///         HookOutput::default()  // Continue without modification
721///     })
722/// });
723/// ```
724pub type HookCallback =
725    Arc<dyn Fn(HookInput, Option<String>, HookContext) -> HookCallbackFuture + Send + Sync>;
726
727/// Hook matcher configuration.
728#[derive(Clone, Default)]
729pub struct HookMatcher {
730    /// Pattern to match (e.g., tool name or regex).
731    pub matcher: Option<String>,
732    /// List of hook callbacks.
733    pub hooks: Vec<HookCallback>,
734    /// Timeout in seconds.
735    pub timeout: Option<f64>,
736}
737
738impl std::fmt::Debug for HookMatcher {
739    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
740        f.debug_struct("HookMatcher")
741            .field("matcher", &self.matcher)
742            .field("hooks", &format!("[{} callbacks]", self.hooks.len()))
743            .field("timeout", &self.timeout)
744            .finish()
745    }
746}
747
748// ============================================================================
749// MCP Server Configuration
750// ============================================================================
751
752/// MCP stdio server configuration.
753#[derive(Debug, Clone, Serialize, Deserialize)]
754pub struct McpStdioServerConfig {
755    /// Server type (always "stdio"). Skipped during serialization since the
756    /// enum tag on `McpServerConfig` already produces the `"type"` field.
757    #[serde(rename = "type", default = "default_stdio", skip_serializing)]
758    pub server_type: String,
759    /// Command to run.
760    pub command: String,
761    /// Command arguments.
762    #[serde(default, skip_serializing_if = "Vec::is_empty")]
763    pub args: Vec<String>,
764    /// Environment variables.
765    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
766    pub env: HashMap<String, String>,
767}
768
769fn default_stdio() -> String {
770    "stdio".to_string()
771}
772
773fn default_sse() -> String {
774    "sse".to_string()
775}
776
777fn default_http() -> String {
778    "http".to_string()
779}
780
781/// MCP SSE server configuration.
782#[derive(Debug, Clone, Serialize, Deserialize)]
783pub struct McpSSEServerConfig {
784    /// Server type (always "sse"). Skipped during serialization since the
785    /// enum tag on `McpServerConfig` already produces the `"type"` field.
786    #[serde(rename = "type", default = "default_sse", skip_serializing)]
787    pub server_type: String,
788    /// Server URL.
789    pub url: String,
790    /// HTTP headers.
791    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
792    pub headers: HashMap<String, String>,
793}
794
795/// MCP HTTP server configuration.
796#[derive(Debug, Clone, Serialize, Deserialize)]
797pub struct McpHttpServerConfig {
798    /// Server type (always "http"). Skipped during serialization since the
799    /// enum tag on `McpServerConfig` already produces the `"type"` field.
800    #[serde(rename = "type", default = "default_http", skip_serializing)]
801    pub server_type: String,
802    /// Server URL.
803    pub url: String,
804    /// HTTP headers.
805    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
806    pub headers: HashMap<String, String>,
807}
808
809/// MCP server configuration union.
810#[derive(Debug, Clone, Serialize, Deserialize)]
811#[serde(tag = "type")]
812pub enum McpServerConfig {
813    /// Stdio-based MCP server.
814    #[serde(rename = "stdio")]
815    Stdio(McpStdioServerConfig),
816    /// SSE-based MCP server.
817    #[serde(rename = "sse")]
818    SSE(McpSSEServerConfig),
819    /// HTTP-based MCP server.
820    #[serde(rename = "http")]
821    Http(McpHttpServerConfig),
822}
823
824/// SDK plugin configuration.
825#[derive(Debug, Clone, Serialize, Deserialize)]
826pub struct SdkPluginConfig {
827    /// Plugin type.
828    #[serde(rename = "type")]
829    pub plugin_type: String,
830    /// Path to the plugin.
831    pub path: String,
832}
833
834// ============================================================================
835// Sandbox Configuration
836// ============================================================================
837
838/// Network configuration for sandbox.
839#[derive(Debug, Clone, Default, Serialize, Deserialize)]
840#[serde(rename_all = "camelCase")]
841pub struct SandboxNetworkConfig {
842    /// Unix socket paths accessible in sandbox.
843    #[serde(default, skip_serializing_if = "Vec::is_empty")]
844    pub allow_unix_sockets: Vec<String>,
845    /// Allow all Unix sockets.
846    #[serde(default)]
847    pub allow_all_unix_sockets: bool,
848    /// Allow binding to localhost ports.
849    #[serde(default)]
850    pub allow_local_binding: bool,
851    /// HTTP proxy port.
852    #[serde(skip_serializing_if = "Option::is_none")]
853    pub http_proxy_port: Option<u16>,
854    /// SOCKS5 proxy port.
855    #[serde(skip_serializing_if = "Option::is_none")]
856    pub socks_proxy_port: Option<u16>,
857}
858
859/// Violations to ignore in sandbox.
860#[derive(Debug, Clone, Default, Serialize, Deserialize)]
861pub struct SandboxIgnoreViolations {
862    /// File paths to ignore.
863    #[serde(default, skip_serializing_if = "Vec::is_empty")]
864    pub file: Vec<String>,
865    /// Network hosts to ignore.
866    #[serde(default, skip_serializing_if = "Vec::is_empty")]
867    pub network: Vec<String>,
868}
869
870/// Sandbox settings configuration.
871#[derive(Debug, Clone, Default, Serialize, Deserialize)]
872#[serde(rename_all = "camelCase")]
873pub struct SandboxSettings {
874    /// Enable bash sandboxing.
875    #[serde(default)]
876    pub enabled: bool,
877    /// Auto-approve bash when sandboxed.
878    #[serde(default = "default_true")]
879    pub auto_allow_bash_if_sandboxed: bool,
880    /// Commands to exclude from sandbox.
881    #[serde(default, skip_serializing_if = "Vec::is_empty")]
882    pub excluded_commands: Vec<String>,
883    /// Allow commands to bypass sandbox.
884    #[serde(default = "default_true")]
885    pub allow_unsandboxed_commands: bool,
886    /// Network configuration.
887    #[serde(default, skip_serializing_if = "Option::is_none")]
888    pub network: Option<SandboxNetworkConfig>,
889    /// Violations to ignore.
890    #[serde(default, skip_serializing_if = "Option::is_none")]
891    pub ignore_violations: Option<SandboxIgnoreViolations>,
892    /// Enable weaker nested sandbox.
893    #[serde(default)]
894    pub enable_weaker_nested_sandbox: bool,
895}
896
897fn default_true() -> bool {
898    true
899}
900
901// ============================================================================
902// Content Block Types
903// ============================================================================
904
905/// Text content block.
906#[derive(Debug, Clone, Serialize, Deserialize)]
907pub struct TextBlock {
908    /// The text content.
909    pub text: String,
910}
911
912/// Thinking content block.
913#[derive(Debug, Clone, Serialize, Deserialize)]
914pub struct ThinkingBlock {
915    /// The thinking content.
916    pub thinking: String,
917    /// Signature for verification.
918    pub signature: String,
919}
920
921/// Tool use content block.
922#[derive(Debug, Clone, Serialize, Deserialize)]
923pub struct ToolUseBlock {
924    /// Tool use ID.
925    pub id: String,
926    /// Tool name.
927    pub name: String,
928    /// Tool input.
929    pub input: serde_json::Value,
930}
931
932/// Tool result content block.
933#[derive(Debug, Clone, Serialize, Deserialize)]
934pub struct ToolResultBlock {
935    /// ID of the tool use this is a result for.
936    pub tool_use_id: String,
937    /// Result content.
938    #[serde(skip_serializing_if = "Option::is_none")]
939    pub content: Option<serde_json::Value>,
940    /// Whether this is an error result.
941    #[serde(skip_serializing_if = "Option::is_none")]
942    pub is_error: Option<bool>,
943}
944
945/// Content block union type.
946#[derive(Debug, Clone, Serialize, Deserialize)]
947#[serde(tag = "type")]
948pub enum ContentBlock {
949    /// Text block.
950    #[serde(rename = "text")]
951    Text(TextBlock),
952    /// Thinking block.
953    #[serde(rename = "thinking")]
954    Thinking(ThinkingBlock),
955    /// Tool use block.
956    #[serde(rename = "tool_use")]
957    ToolUse(ToolUseBlock),
958    /// Tool result block.
959    #[serde(rename = "tool_result")]
960    ToolResult(ToolResultBlock),
961}
962
963impl ContentBlock {
964    /// Get the text if this is a text block.
965    pub fn as_text(&self) -> Option<&str> {
966        match self {
967            ContentBlock::Text(block) => Some(&block.text),
968            _ => None,
969        }
970    }
971
972    /// Check if this is a tool use block.
973    pub fn is_tool_use(&self) -> bool {
974        matches!(self, ContentBlock::ToolUse(_))
975    }
976}
977
978// ============================================================================
979// Message Types
980// ============================================================================
981
982/// Assistant message error types.
983#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
984#[serde(rename_all = "snake_case")]
985pub enum AssistantMessageError {
986    /// Authentication failed.
987    AuthenticationFailed,
988    /// Billing error.
989    BillingError,
990    /// Rate limit exceeded.
991    RateLimit,
992    /// Invalid request.
993    InvalidRequest,
994    /// Server error.
995    ServerError,
996    /// Unknown error.
997    Unknown,
998}
999
1000/// User message.
1001#[derive(Debug, Clone, Serialize, Deserialize)]
1002pub struct UserMessage {
1003    /// Message content (string or content blocks).
1004    pub content: UserMessageContent,
1005    /// Unique identifier.
1006    #[serde(skip_serializing_if = "Option::is_none")]
1007    pub uuid: Option<String>,
1008    /// Parent tool use ID if this is a tool result.
1009    #[serde(skip_serializing_if = "Option::is_none")]
1010    pub parent_tool_use_id: Option<String>,
1011}
1012
1013/// User message content can be a string or content blocks.
1014#[derive(Debug, Clone, Serialize, Deserialize)]
1015#[serde(untagged)]
1016pub enum UserMessageContent {
1017    /// Plain text content.
1018    Text(String),
1019    /// Content blocks.
1020    Blocks(Vec<ContentBlock>),
1021}
1022
1023impl UserMessage {
1024    /// Get text content if this is a simple text message.
1025    pub fn text(&self) -> Option<&str> {
1026        match &self.content {
1027            UserMessageContent::Text(s) => Some(s),
1028            UserMessageContent::Blocks(blocks) => {
1029                if blocks.len() == 1 {
1030                    blocks[0].as_text()
1031                } else {
1032                    None
1033                }
1034            }
1035        }
1036    }
1037}
1038
1039/// Assistant message.
1040#[derive(Debug, Clone, Serialize, Deserialize)]
1041pub struct AssistantMessage {
1042    /// Content blocks.
1043    pub content: Vec<ContentBlock>,
1044    /// Model that generated this message.
1045    pub model: String,
1046    /// Parent tool use ID.
1047    #[serde(skip_serializing_if = "Option::is_none")]
1048    pub parent_tool_use_id: Option<String>,
1049    /// Error if the message failed.
1050    #[serde(skip_serializing_if = "Option::is_none")]
1051    pub error: Option<AssistantMessageError>,
1052}
1053
1054impl AssistantMessage {
1055    /// Get all text content from this message.
1056    pub fn text(&self) -> String {
1057        self.content
1058            .iter()
1059            .filter_map(|block| block.as_text())
1060            .collect::<Vec<_>>()
1061            .join("")
1062    }
1063
1064    /// Get all tool use blocks.
1065    pub fn tool_uses(&self) -> Vec<&ToolUseBlock> {
1066        self.content
1067            .iter()
1068            .filter_map(|block| match block {
1069                ContentBlock::ToolUse(tu) => Some(tu),
1070                _ => None,
1071            })
1072            .collect()
1073    }
1074}
1075
1076/// System message.
1077#[derive(Debug, Clone, Serialize, Deserialize)]
1078pub struct SystemMessage {
1079    /// Message subtype.
1080    pub subtype: String,
1081    /// Message data.
1082    pub data: serde_json::Value,
1083}
1084
1085/// Result message with cost and usage information.
1086#[derive(Debug, Clone, Serialize, Deserialize)]
1087pub struct ResultMessage {
1088    /// Message subtype.
1089    pub subtype: String,
1090    /// Duration in milliseconds.
1091    pub duration_ms: u64,
1092    /// API duration in milliseconds.
1093    pub duration_api_ms: u64,
1094    /// Whether the result is an error.
1095    pub is_error: bool,
1096    /// Number of turns in the conversation.
1097    pub num_turns: u32,
1098    /// Session ID.
1099    pub session_id: String,
1100    /// Total cost in USD.
1101    #[serde(skip_serializing_if = "Option::is_none")]
1102    pub total_cost_usd: Option<f64>,
1103    /// Token usage information.
1104    #[serde(skip_serializing_if = "Option::is_none")]
1105    pub usage: Option<serde_json::Value>,
1106    /// Result text.
1107    #[serde(skip_serializing_if = "Option::is_none")]
1108    pub result: Option<String>,
1109    /// Structured output if output_format was specified.
1110    #[serde(skip_serializing_if = "Option::is_none")]
1111    pub structured_output: Option<serde_json::Value>,
1112}
1113
1114/// Stream event for partial message updates.
1115#[derive(Debug, Clone, Serialize, Deserialize)]
1116pub struct StreamEvent {
1117    /// Event UUID.
1118    pub uuid: String,
1119    /// Session ID.
1120    pub session_id: String,
1121    /// Raw Anthropic API stream event.
1122    pub event: serde_json::Value,
1123    /// Parent tool use ID.
1124    #[serde(skip_serializing_if = "Option::is_none")]
1125    pub parent_tool_use_id: Option<String>,
1126}
1127
1128/// Message union type.
1129#[derive(Debug, Clone, Serialize, Deserialize)]
1130#[serde(tag = "type")]
1131pub enum Message {
1132    /// User message.
1133    #[serde(rename = "user")]
1134    User(UserMessage),
1135    /// Assistant message.
1136    #[serde(rename = "assistant")]
1137    Assistant(AssistantMessage),
1138    /// System message.
1139    #[serde(rename = "system")]
1140    System(SystemMessage),
1141    /// Result message.
1142    #[serde(rename = "result")]
1143    Result(ResultMessage),
1144    /// Stream event.
1145    #[serde(rename = "stream_event")]
1146    StreamEvent(StreamEvent),
1147}
1148
1149impl Message {
1150    /// Check if this is a result message.
1151    pub fn is_result(&self) -> bool {
1152        matches!(self, Message::Result(_))
1153    }
1154
1155    /// Check if this is an assistant message.
1156    pub fn is_assistant(&self) -> bool {
1157        matches!(self, Message::Assistant(_))
1158    }
1159
1160    /// Get as assistant message if applicable.
1161    pub fn as_assistant(&self) -> Option<&AssistantMessage> {
1162        match self {
1163            Message::Assistant(msg) => Some(msg),
1164            _ => None,
1165        }
1166    }
1167
1168    /// Get as result message if applicable.
1169    pub fn as_result(&self) -> Option<&ResultMessage> {
1170        match self {
1171            Message::Result(msg) => Some(msg),
1172            _ => None,
1173        }
1174    }
1175}
1176
1177// ============================================================================
1178// Agent Configuration
1179// ============================================================================
1180
1181/// System prompt preset.
1182#[derive(Debug, Clone, Serialize, Deserialize)]
1183pub struct SystemPromptPreset {
1184    /// Type (always "preset").
1185    #[serde(rename = "type")]
1186    pub preset_type: String,
1187    /// Preset name.
1188    pub preset: String,
1189    /// Text to append.
1190    #[serde(skip_serializing_if = "Option::is_none")]
1191    pub append: Option<String>,
1192}
1193
1194/// Tools preset.
1195#[derive(Debug, Clone, Serialize, Deserialize)]
1196pub struct ToolsPreset {
1197    /// Type (always "preset").
1198    #[serde(rename = "type")]
1199    pub preset_type: String,
1200    /// Preset name.
1201    pub preset: String,
1202}
1203
1204/// System prompt configuration.
1205#[derive(Debug, Clone, Serialize, Deserialize)]
1206#[serde(untagged)]
1207pub enum SystemPromptConfig {
1208    /// Plain text system prompt.
1209    Text(String),
1210    /// Preset configuration.
1211    Preset(SystemPromptPreset),
1212}
1213
1214/// Tools configuration.
1215#[derive(Debug, Clone, Serialize, Deserialize)]
1216#[serde(untagged)]
1217pub enum ToolsConfig {
1218    /// List of tool names.
1219    List(Vec<String>),
1220    /// Preset configuration.
1221    Preset(ToolsPreset),
1222}
1223
1224/// Agent model.
1225#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1226#[serde(rename_all = "lowercase")]
1227pub enum AgentModel {
1228    /// Sonnet model.
1229    Sonnet,
1230    /// Opus model.
1231    Opus,
1232    /// Haiku model.
1233    Haiku,
1234    /// Inherit from parent.
1235    Inherit,
1236}
1237
1238/// Agent definition.
1239#[derive(Debug, Clone, Serialize, Deserialize)]
1240pub struct AgentDefinition {
1241    /// Agent description.
1242    pub description: String,
1243    /// Agent prompt.
1244    pub prompt: String,
1245    /// Allowed tools.
1246    #[serde(skip_serializing_if = "Option::is_none")]
1247    pub tools: Option<Vec<String>>,
1248    /// Model to use.
1249    #[serde(skip_serializing_if = "Option::is_none")]
1250    pub model: Option<AgentModel>,
1251}
1252
1253/// Setting source.
1254#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1255#[serde(rename_all = "lowercase")]
1256pub enum SettingSource {
1257    /// User settings.
1258    User,
1259    /// Project settings.
1260    Project,
1261    /// Local settings.
1262    Local,
1263}
1264
1265/// SDK Beta features.
1266#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1267#[serde(rename_all = "kebab-case")]
1268pub enum SdkBeta {
1269    /// Extended context beta.
1270    #[serde(rename = "context-1m-2025-08-07")]
1271    Context1m,
1272}
1273
1274/// MCP servers configuration.
1275#[derive(Debug, Clone)]
1276pub enum McpServersConfig {
1277    /// Map of server configurations.
1278    Map(HashMap<String, McpServerConfig>),
1279    /// Path to configuration file.
1280    Path(PathBuf),
1281}
1282
1283impl Default for McpServersConfig {
1284    fn default() -> Self {
1285        Self::Map(HashMap::new())
1286    }
1287}
1288
1289/// Configuration for extended thinking behavior.
1290#[derive(Debug, Clone, Serialize, Deserialize)]
1291#[serde(tag = "type", rename_all = "lowercase")]
1292pub enum ThinkingConfig {
1293    /// Adaptive thinking — automatically determines thinking depth.
1294    Adaptive,
1295    /// Enabled with a specific token budget.
1296    Enabled {
1297        /// Maximum number of tokens for thinking.
1298        budget_tokens: u32,
1299    },
1300    /// Thinking disabled.
1301    Disabled,
1302}
1303
1304/// Effort level for thinking depth.
1305#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1306#[serde(rename_all = "lowercase")]
1307pub enum Effort {
1308    /// Low effort — minimal thinking.
1309    Low,
1310    /// Medium effort — moderate thinking.
1311    Medium,
1312    /// High effort — thorough thinking.
1313    High,
1314    /// Max effort — maximum thinking depth.
1315    Max,
1316}
1317
1318impl std::fmt::Display for Effort {
1319    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1320        match self {
1321            Self::Low => write!(f, "low"),
1322            Self::Medium => write!(f, "medium"),
1323            Self::High => write!(f, "high"),
1324            Self::Max => write!(f, "max"),
1325        }
1326    }
1327}
1328
1329/// Query options for Claude SDK.
1330#[derive(Clone, Default)]
1331pub struct ClaudeAgentOptions {
1332    /// Tools to use.
1333    pub tools: Option<ToolsConfig>,
1334    /// Allowed tools.
1335    pub allowed_tools: Vec<String>,
1336    /// System prompt.
1337    pub system_prompt: Option<SystemPromptConfig>,
1338    /// MCP server configurations.
1339    pub mcp_servers: McpServersConfig,
1340    /// Permission mode.
1341    pub permission_mode: Option<PermissionMode>,
1342    /// Continue previous conversation.
1343    pub continue_conversation: bool,
1344    /// Resume session ID.
1345    pub resume: Option<String>,
1346    /// Maximum turns.
1347    pub max_turns: Option<u32>,
1348    /// Maximum budget in USD.
1349    pub max_budget_usd: Option<f64>,
1350    /// Disallowed tools.
1351    pub disallowed_tools: Vec<String>,
1352    /// Model to use.
1353    pub model: Option<String>,
1354    /// Fallback model.
1355    pub fallback_model: Option<String>,
1356    /// Beta features.
1357    pub betas: Vec<SdkBeta>,
1358    /// Permission prompt tool name.
1359    pub permission_prompt_tool_name: Option<String>,
1360    /// Working directory.
1361    pub cwd: Option<PathBuf>,
1362    /// Path to CLI executable.
1363    pub cli_path: Option<PathBuf>,
1364    /// Settings string.
1365    pub settings: Option<String>,
1366    /// Additional directories.
1367    pub add_dirs: Vec<PathBuf>,
1368    /// Environment variables.
1369    pub env: HashMap<String, String>,
1370    /// Extra CLI arguments.
1371    pub extra_args: HashMap<String, Option<String>>,
1372    /// Maximum buffer size for stdout.
1373    pub max_buffer_size: Option<usize>,
1374    /// Callback for stderr output.
1375    pub stderr: Option<Arc<dyn Fn(String) + Send + Sync>>,
1376    /// Tool permission callback.
1377    pub can_use_tool: Option<CanUseTool>,
1378    /// Hook configurations.
1379    pub hooks: Option<HashMap<HookEvent, Vec<HookMatcher>>>,
1380    /// User identifier.
1381    pub user: Option<String>,
1382    /// Include partial messages in stream.
1383    pub include_partial_messages: bool,
1384    /// Fork session when resuming.
1385    pub fork_session: bool,
1386    /// Agent definitions.
1387    pub agents: Option<HashMap<String, AgentDefinition>>,
1388    /// Setting sources.
1389    pub setting_sources: Option<Vec<SettingSource>>,
1390    /// Sandbox settings.
1391    pub sandbox: Option<SandboxSettings>,
1392    /// Plugin configurations.
1393    pub plugins: Vec<SdkPluginConfig>,
1394    /// Maximum thinking tokens. Deprecated: use `thinking` instead.
1395    pub max_thinking_tokens: Option<u32>,
1396    /// Controls extended thinking behavior. Takes precedence over `max_thinking_tokens`.
1397    pub thinking: Option<ThinkingConfig>,
1398    /// Effort level for thinking depth.
1399    pub effort: Option<Effort>,
1400    /// Output format for structured outputs.
1401    pub output_format: Option<serde_json::Value>,
1402    /// Enable file checkpointing.
1403    pub enable_file_checkpointing: bool,
1404    /// Timeout in seconds for CLI operations (default: 300 = 5 minutes).
1405    /// Set to 0 to disable timeout.
1406    pub timeout_secs: Option<u64>,
1407}
1408
1409impl std::fmt::Debug for ClaudeAgentOptions {
1410    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1411        f.debug_struct("ClaudeAgentOptions")
1412            .field("tools", &self.tools)
1413            .field("allowed_tools", &self.allowed_tools)
1414            .field("system_prompt", &self.system_prompt)
1415            .field("permission_mode", &self.permission_mode)
1416            .field("continue_conversation", &self.continue_conversation)
1417            .field("resume", &self.resume)
1418            .field("max_turns", &self.max_turns)
1419            .field("max_budget_usd", &self.max_budget_usd)
1420            .field("disallowed_tools", &self.disallowed_tools)
1421            .field("model", &self.model)
1422            .field(
1423                "can_use_tool",
1424                &self.can_use_tool.as_ref().map(|_| "<callback>"),
1425            )
1426            .field(
1427                "hooks",
1428                &self.hooks.as_ref().map(|h| format!("{} events", h.len())),
1429            )
1430            .field("stderr", &self.stderr.as_ref().map(|_| "<callback>"))
1431            .finish_non_exhaustive()
1432    }
1433}
1434
1435impl ClaudeAgentOptions {
1436    /// Create new options with defaults.
1437    pub fn new() -> Self {
1438        Self::default()
1439    }
1440
1441    /// Set the system prompt.
1442    pub fn with_system_prompt(mut self, prompt: impl Into<String>) -> Self {
1443        self.system_prompt = Some(SystemPromptConfig::Text(prompt.into()));
1444        self
1445    }
1446
1447    /// Set the model.
1448    pub fn with_model(mut self, model: impl Into<String>) -> Self {
1449        self.model = Some(model.into());
1450        self
1451    }
1452
1453    /// Set the permission mode.
1454    pub fn with_permission_mode(mut self, mode: PermissionMode) -> Self {
1455        self.permission_mode = Some(mode);
1456        self
1457    }
1458
1459    /// Set max turns.
1460    pub fn with_max_turns(mut self, turns: u32) -> Self {
1461        self.max_turns = Some(turns);
1462        self
1463    }
1464
1465    /// Set working directory.
1466    pub fn with_cwd(mut self, cwd: impl Into<PathBuf>) -> Self {
1467        self.cwd = Some(cwd.into());
1468        self
1469    }
1470
1471    /// Set allowed tools.
1472    pub fn with_allowed_tools(mut self, tools: Vec<String>) -> Self {
1473        self.allowed_tools = tools;
1474        self
1475    }
1476
1477    /// Enable partial message streaming.
1478    pub fn with_partial_messages(mut self) -> Self {
1479        self.include_partial_messages = true;
1480        self
1481    }
1482
1483    /// Set thinking configuration. Takes precedence over `max_thinking_tokens`.
1484    pub fn with_thinking(mut self, thinking: ThinkingConfig) -> Self {
1485        self.thinking = Some(thinking);
1486        self
1487    }
1488
1489    /// Set effort level for thinking depth.
1490    pub fn with_effort(mut self, effort: Effort) -> Self {
1491        self.effort = Some(effort);
1492        self
1493    }
1494
1495    /// Set the timeout for CLI operations in seconds.
1496    ///
1497    /// Default is 300 seconds (5 minutes). Set to 0 to disable timeout.
1498    /// This timeout applies to:
1499    /// - Initial CLI process startup
1500    /// - Control protocol requests (initialize, can_use_tool, etc.)
1501    /// - Waiting for CLI responses
1502    pub fn with_timeout_secs(mut self, timeout: u64) -> Self {
1503        self.timeout_secs = Some(timeout);
1504        self
1505    }
1506
1507    /// Set the can_use_tool callback.
1508    pub fn with_can_use_tool<F, Fut>(mut self, callback: F) -> Self
1509    where
1510        F: Fn(String, serde_json::Value, ToolPermissionContext) -> Fut + Send + Sync + 'static,
1511        Fut: Future<Output = PermissionResult> + Send + 'static,
1512    {
1513        self.can_use_tool = Some(Arc::new(move |name, input, ctx| {
1514            Box::pin(callback(name, input, ctx))
1515        }));
1516        self
1517    }
1518}
1519
1520// ============================================================================
1521// Control Protocol Types
1522// ============================================================================
1523
1524/// Control request subtypes.
1525#[derive(Debug, Clone, Serialize, Deserialize)]
1526#[serde(tag = "subtype")]
1527pub enum ControlRequestPayload {
1528    /// Interrupt request.
1529    #[serde(rename = "interrupt")]
1530    Interrupt,
1531    /// Tool permission request.
1532    #[serde(rename = "can_use_tool")]
1533    CanUseTool {
1534        /// Tool name.
1535        tool_name: String,
1536        /// Tool input.
1537        input: serde_json::Value,
1538        /// Permission suggestions.
1539        permission_suggestions: Option<Vec<serde_json::Value>>,
1540        /// Blocked path.
1541        blocked_path: Option<String>,
1542    },
1543    /// Initialize request.
1544    #[serde(rename = "initialize")]
1545    Initialize {
1546        /// Hook configurations.
1547        hooks: Option<serde_json::Value>,
1548        /// Agent definitions.
1549        #[serde(skip_serializing_if = "Option::is_none")]
1550        agents: Option<serde_json::Value>,
1551    },
1552    /// Set permission mode request.
1553    #[serde(rename = "set_permission_mode")]
1554    SetPermissionMode {
1555        /// New mode.
1556        mode: String,
1557    },
1558    /// Set model request.
1559    #[serde(rename = "set_model")]
1560    SetModel {
1561        /// New model.
1562        model: String,
1563    },
1564    /// Hook callback request.
1565    #[serde(rename = "hook_callback")]
1566    HookCallback {
1567        /// Callback ID.
1568        callback_id: String,
1569        /// Hook input.
1570        input: serde_json::Value,
1571        /// Tool use ID.
1572        tool_use_id: Option<String>,
1573    },
1574    /// MCP message request.
1575    #[serde(rename = "mcp_message")]
1576    McpMessage {
1577        /// Server name.
1578        server_name: String,
1579        /// JSONRPC message.
1580        message: serde_json::Value,
1581    },
1582    /// MCP status request.
1583    #[serde(rename = "mcp_status")]
1584    McpStatus,
1585    /// Rewind files request.
1586    #[serde(rename = "rewind_files")]
1587    RewindFiles {
1588        /// User message ID to rewind to.
1589        user_message_id: String,
1590    },
1591}
1592
1593/// Control request.
1594#[derive(Debug, Clone, Serialize, Deserialize)]
1595pub struct ControlRequest {
1596    /// Always "control_request".
1597    #[serde(rename = "type")]
1598    pub request_type: String,
1599    /// Request ID.
1600    pub request_id: String,
1601    /// Request payload.
1602    pub request: ControlRequestPayload,
1603}
1604
1605/// Success response.
1606#[derive(Debug, Clone, Serialize, Deserialize)]
1607pub struct ControlSuccessResponse {
1608    /// Always "success".
1609    pub subtype: String,
1610    /// Request ID.
1611    pub request_id: String,
1612    /// Response data.
1613    pub response: Option<serde_json::Value>,
1614}
1615
1616/// Error response.
1617#[derive(Debug, Clone, Serialize, Deserialize)]
1618pub struct ControlErrorResponse {
1619    /// Always "error".
1620    pub subtype: String,
1621    /// Request ID.
1622    pub request_id: String,
1623    /// Error message.
1624    pub error: String,
1625}
1626
1627/// Control response payload.
1628#[derive(Debug, Clone, Serialize, Deserialize)]
1629#[serde(tag = "subtype")]
1630pub enum ControlResponsePayload {
1631    /// Success response.
1632    #[serde(rename = "success")]
1633    Success {
1634        /// Request ID.
1635        request_id: String,
1636        /// Response data.
1637        response: Option<serde_json::Value>,
1638    },
1639    /// Error response.
1640    #[serde(rename = "error")]
1641    Error {
1642        /// Request ID.
1643        request_id: String,
1644        /// Error message.
1645        error: String,
1646    },
1647}
1648
1649/// Control response.
1650#[derive(Debug, Clone, Serialize, Deserialize)]
1651pub struct ControlResponse {
1652    /// Always "control_response".
1653    #[serde(rename = "type")]
1654    pub response_type: String,
1655    /// Response payload.
1656    pub response: ControlResponsePayload,
1657}
1658
1659impl ControlResponse {
1660    /// Get the request ID.
1661    pub fn request_id(&self) -> &str {
1662        match &self.response {
1663            ControlResponsePayload::Success { request_id, .. } => request_id,
1664            ControlResponsePayload::Error { request_id, .. } => request_id,
1665        }
1666    }
1667
1668    /// Check if this is a success response.
1669    pub fn is_success(&self) -> bool {
1670        matches!(&self.response, ControlResponsePayload::Success { .. })
1671    }
1672
1673    /// Get the response data if successful.
1674    pub fn data(&self) -> Option<&serde_json::Value> {
1675        match &self.response {
1676            ControlResponsePayload::Success { response, .. } => response.as_ref(),
1677            ControlResponsePayload::Error { .. } => None,
1678        }
1679    }
1680
1681    /// Get the error message if failed.
1682    pub fn error(&self) -> Option<&str> {
1683        match &self.response {
1684            ControlResponsePayload::Success { .. } => None,
1685            ControlResponsePayload::Error { error, .. } => Some(error),
1686        }
1687    }
1688}
1689
1690#[cfg(test)]
1691mod tests {
1692    use super::*;
1693
1694    #[test]
1695    fn test_permission_result_allow() {
1696        let result = PermissionResult::allow();
1697        let json = serde_json::to_string(&result).unwrap();
1698        assert!(json.contains("allow"));
1699    }
1700
1701    #[test]
1702    fn test_message_parsing() {
1703        let json = r#"{"type": "assistant", "content": [{"type": "text", "text": "Hello"}], "model": "claude-3"}"#;
1704        let msg: Message = serde_json::from_str(json).unwrap();
1705        assert!(msg.is_assistant());
1706    }
1707
1708    #[test]
1709    fn test_content_block_text() {
1710        let block = ContentBlock::Text(TextBlock {
1711            text: "Hello".to_string(),
1712        });
1713        assert_eq!(block.as_text(), Some("Hello"));
1714    }
1715
1716    #[test]
1717    fn test_options_builder() {
1718        let opts = ClaudeAgentOptions::new()
1719            .with_model("claude-3-sonnet")
1720            .with_max_turns(5)
1721            .with_permission_mode(PermissionMode::AcceptEdits);
1722
1723        assert_eq!(opts.model, Some("claude-3-sonnet".to_string()));
1724        assert_eq!(opts.max_turns, Some(5));
1725        assert_eq!(opts.permission_mode, Some(PermissionMode::AcceptEdits));
1726    }
1727}