iflow_cli_sdk_rust/
types.rs

1//! Type definitions for iFlow SDK
2//!
3//! This module contains all the type definitions used throughout the iFlow SDK,
4//! including configuration options and message types.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::path::PathBuf;
9use std::time::Duration;
10
11// Import logger configuration
12use super::logger::LoggerConfig;
13
14// Re-export the types we need from agent-client-protocol
15pub use agent_client_protocol::{
16    ContentBlock, EnvVariable, Error, ImageContent, McpServer, Plan, SessionId, StopReason,
17    TextContent, ToolCall, ToolCallUpdate,
18};
19
20/// Protocol version
21pub const PROTOCOL_VERSION: u32 = 1;
22
23/// Permission mode for tool calls
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
25pub enum PermissionMode {
26    /// Automatically approve all tool calls
27    #[serde(rename = "auto")]
28    Auto,
29    /// Require manual confirmation for all tool calls
30    #[serde(rename = "manual")]
31    Manual,
32    /// Auto-approve certain types of tool calls
33    #[serde(rename = "selective")]
34    Selective,
35}
36
37impl Default for PermissionMode {
38    fn default() -> Self {
39        PermissionMode::Auto
40    }
41}
42
43/// Tool call status
44#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
45pub enum ToolCallStatus {
46    /// The tool call is pending
47    #[serde(rename = "pending")]
48    Pending,
49    /// The tool call is in progress
50    #[serde(rename = "in_progress")]
51    InProgress,
52    /// The tool call has completed successfully
53    #[serde(rename = "completed")]
54    Completed,
55    /// The tool call has failed
56    #[serde(rename = "failed")]
57    Failed,
58    /// Legacy alias for InProgress
59    #[serde(rename = "running")]
60    Running,
61    /// Legacy alias for Completed
62    #[serde(rename = "finished")]
63    Finished,
64    /// Legacy alias for Failed
65    #[serde(rename = "error")]
66    Error,
67}
68
69/// Plan entry priority levels
70#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
71pub enum PlanPriority {
72    #[serde(rename = "high")]
73    High,
74    #[serde(rename = "medium")]
75    Medium,
76    #[serde(rename = "low")]
77    Low,
78}
79
80impl Default for PlanPriority {
81    fn default() -> Self {
82        PlanPriority::Medium
83    }
84}
85
86/// Plan entry status
87#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
88pub enum PlanStatus {
89    #[serde(rename = "pending")]
90    Pending,
91    #[serde(rename = "in_progress")]
92    InProgress,
93    #[serde(rename = "completed")]
94    Completed,
95}
96
97impl Default for PlanStatus {
98    fn default() -> Self {
99        PlanStatus::Pending
100    }
101}
102
103/// Plan entry for task planning
104#[derive(Debug, Clone, Serialize, Deserialize, Default)]
105pub struct PlanEntry {
106    /// The content of the plan entry
107    pub content: String,
108    /// The priority of the plan entry
109    #[serde(default)]
110    pub priority: PlanPriority,
111    /// The status of the plan entry
112    #[serde(default)]
113    pub status: PlanStatus,
114}
115
116/// User message chunk
117///
118/// A chunk of a user message, which can be either text or a file path.
119#[derive(Debug, Clone, Serialize, Deserialize)]
120#[serde(untagged)]
121pub enum UserMessageChunk {
122    /// Text content
123    Text {
124        #[serde(rename = "text")]
125        content: String,
126    },
127    /// File path content
128    Path {
129        #[serde(rename = "path")]
130        path: PathBuf,
131    },
132}
133
134/// User message
135///
136/// A user message consisting of one or more chunks.
137#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct UserMessage {
139    /// The type of the message (always "user")
140    #[serde(rename = "type")]
141    pub message_type: String,
142    /// The chunks of the message
143    pub chunks: Vec<UserMessageChunk>,
144}
145
146impl UserMessage {
147    /// Create a new user message with text content
148    ///
149    /// # Arguments
150    /// * `text` - The text content of the message
151    ///
152    /// # Returns
153    /// A new UserMessage instance
154    pub fn new_text(text: String) -> Self {
155        Self {
156            message_type: "user".to_string(),
157            chunks: vec![UserMessageChunk::Text { content: text }],
158        }
159    }
160
161    /// Create a new user message with a file path
162    ///
163    /// # Arguments
164    /// * `path` - The path to the file
165    ///
166    /// # Returns
167    /// A new UserMessage instance
168    pub fn new_path(path: PathBuf) -> Self {
169        Self {
170            message_type: "user".to_string(),
171            chunks: vec![UserMessageChunk::Path { path }],
172        }
173    }
174
175    /// Create a new user message with multiple chunks
176    ///
177    /// # Arguments
178    /// * `chunks` - The chunks of the message
179    ///
180    /// # Returns
181    /// A new UserMessage instance
182    pub fn new(chunks: Vec<UserMessageChunk>) -> Self {
183        Self {
184            message_type: "user".to_string(),
185            chunks,
186        }
187    }
188}
189
190/// Icon for tool calls
191#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct Icon {
193    /// The type of the icon
194    #[serde(rename = "type")]
195    pub icon_type: String,
196    /// The value of the icon
197    pub value: String,
198}
199
200/// Tool call confirmation details
201#[derive(Debug, Clone, Serialize, Deserialize)]
202pub struct ToolCallConfirmation {
203    /// The type of the confirmation
204    #[serde(rename = "type")]
205    pub confirmation_type: String,
206    /// Optional description
207    pub description: Option<String>,
208    /// Command for execute type
209    pub command: Option<String>,
210    /// Root command for execute type
211    #[serde(rename = "rootCommand")]
212    pub root_command: Option<String>,
213    /// Server name for mcp type
214    #[serde(rename = "serverName")]
215    pub server_name: Option<String>,
216    /// Tool name for mcp type
217    #[serde(rename = "toolName")]
218    pub tool_name: Option<String>,
219    /// Tool display name for mcp type
220    #[serde(rename = "toolDisplayName")]
221    pub tool_display_name: Option<String>,
222    /// URLs for fetch type
223    pub urls: Option<Vec<String>>,
224}
225
226/// Tool call content
227#[derive(Debug, Clone, Serialize, Deserialize)]
228pub struct ToolCallContent {
229    /// The type of the content
230    #[serde(rename = "type")]
231    pub content_type: String,
232    /// Markdown content for markdown type
233    pub markdown: Option<String>,
234    /// Path for diff type
235    pub path: Option<String>,
236    /// Old text for diff type
237    #[serde(rename = "oldText")]
238    pub old_text: Option<String>,
239    /// New text for diff type
240    #[serde(rename = "newText")]
241    pub new_text: Option<String>,
242}
243
244/// File location for a tool call
245#[derive(Debug, Clone, Serialize, Deserialize)]
246pub struct ToolCallLocation {
247    /// The path of the file
248    pub path: String,
249    /// The start line (optional)
250    #[serde(rename = "lineStart")]
251    pub line_start: Option<u32>,
252    /// The end line (optional)
253    #[serde(rename = "lineEnd")]
254    pub line_end: Option<u32>,
255}
256
257/// Agent information
258#[derive(Debug, Clone, Serialize, Deserialize)]
259pub struct AgentInfo {
260    /// Raw agentId from iFlow ACP
261    #[serde(rename = "agentId")]
262    pub agent_id: String,
263    /// Agent index within task
264    #[serde(rename = "agentIndex")]
265    pub agent_index: Option<u32>,
266    /// Task/call ID from agentId
267    #[serde(rename = "taskId")]
268    pub task_id: Option<String>,
269    /// Creation/event timestamp
270    pub timestamp: Option<u64>,
271}
272
273/// Tool call message
274#[derive(Debug, Clone, Serialize, Deserialize)]
275pub struct ToolCallMessage {
276    /// The type of the message (always "tool_call")
277    #[serde(rename = "type")]
278    pub message_type: String,
279    /// The ID of the tool call
280    pub id: String,
281    /// The label of the tool call
282    pub label: String,
283    /// The icon of the tool call
284    pub icon: Icon,
285    /// The status of the tool call
286    pub status: ToolCallStatus,
287    /// The name of the tool (optional)
288    #[serde(rename = "toolName")]
289    pub tool_name: Option<String>,
290    /// The content of the tool call (optional)
291    pub content: Option<ToolCallContent>,
292    /// The locations of the tool call (optional)
293    pub locations: Option<Vec<ToolCallLocation>>,
294    /// The confirmation details of the tool call (optional)
295    pub confirmation: Option<ToolCallConfirmation>,
296    /// The agent ID (optional)
297    #[serde(rename = "agentId")]
298    pub agent_id: Option<String>,
299    /// The agent information (optional)
300    #[serde(rename = "agentInfo")]
301    pub agent_info: Option<AgentInfo>,
302}
303
304impl ToolCallMessage {
305    /// Create a new tool call message
306    ///
307    /// # Arguments
308    /// * `id` - The ID of the tool call
309    /// * `label` - The label of the tool call
310    /// * `icon` - The icon of the tool call
311    /// * `status` - The status of the tool call
312    ///
313    /// # Returns
314    /// A new ToolCallMessage instance
315    pub fn new(id: String, label: String, icon: Icon, status: ToolCallStatus) -> Self {
316        Self {
317            message_type: "tool_call".to_string(),
318            id,
319            label,
320            icon,
321            status,
322            tool_name: None,
323            content: None,
324            locations: None,
325            confirmation: None,
326            agent_id: None,
327            agent_info: None,
328        }
329    }
330}
331
332/// Configuration for WebSocket connection
333#[derive(Debug, Clone)]
334pub struct WebSocketConfig {
335    /// WebSocket URL to connect to (None means auto-generate in auto-start mode)
336    pub url: Option<String>,
337    /// Number of reconnect attempts
338    pub reconnect_attempts: u32,
339    /// Interval between reconnect attempts
340    pub reconnect_interval: Duration,
341}
342
343impl Default for WebSocketConfig {
344    fn default() -> Self {
345        Self {
346            url: Some("ws://localhost:8090/acp?peer=iflow".to_string()),
347            reconnect_attempts: 3,
348            reconnect_interval: Duration::from_secs(5),
349        }
350    }
351}
352
353impl WebSocketConfig {
354    /// Create a new WebSocketConfig with the specified URL and default reconnect settings
355    pub fn new(url: String) -> Self {
356        Self {
357            url: Some(url),
358            ..Default::default()
359        }
360    }
361
362    /// Create a new WebSocketConfig for auto-start mode (URL will be auto-generated)
363    pub fn auto_start() -> Self {
364        Self {
365            url: None,
366            ..Default::default()
367        }
368    }
369
370    /// Create a new WebSocketConfig with custom reconnect settings
371    pub fn with_reconnect_settings(
372        url: String,
373        reconnect_attempts: u32,
374        reconnect_interval: Duration,
375    ) -> Self {
376        Self {
377            url: Some(url),
378            reconnect_attempts,
379            reconnect_interval,
380        }
381    }
382
383    /// Create a new WebSocketConfig for auto-start mode with custom reconnect settings
384    pub fn auto_start_with_reconnect_settings(
385        reconnect_attempts: u32,
386        reconnect_interval: Duration,
387    ) -> Self {
388        Self {
389            url: None,
390            reconnect_attempts,
391            reconnect_interval,
392        }
393    }
394}
395
396/// Configuration for file access
397#[derive(Debug, Clone)]
398pub struct FileAccessConfig {
399    /// Whether file access is enabled
400    pub enabled: bool,
401    /// Allowed directories for file access
402    pub allowed_dirs: Option<Vec<PathBuf>>,
403    /// Whether file access is read-only
404    pub read_only: bool,
405    /// Maximum file size for file access
406    pub max_size: u64,
407}
408
409impl Default for FileAccessConfig {
410    fn default() -> Self {
411        Self {
412            enabled: false,
413            allowed_dirs: None,
414            read_only: false,
415            max_size: 10 * 1024 * 1024, // 10MB
416        }
417    }
418}
419
420/// Configuration for process management
421#[derive(Debug, Clone)]
422pub struct ProcessConfig {
423    /// Whether to automatically start the iFlow process
424    pub auto_start: bool,
425    /// Port to start the iFlow process on (only used in auto-start WebSocket mode)
426    pub start_port: Option<u16>,
427    /// Whether to start the iFlow process in debug mode
428    pub debug: bool,
429}
430
431impl Default for ProcessConfig {
432    fn default() -> Self {
433        Self {
434            auto_start: true,
435            start_port: None, // No port needed for stdio mode
436            debug: false,
437        }
438    }
439}
440
441impl ProcessConfig {
442    /// Create a new ProcessConfig with default values
443    pub fn new() -> Self {
444        Self::default()
445    }
446
447    /// Set whether to automatically start the iFlow process
448    pub fn auto_start(mut self, auto_start: bool) -> Self {
449        self.auto_start = auto_start;
450        self
451    }
452
453    /// Set the port to start the iFlow process on (only used in WebSocket mode)
454    pub fn start_port(mut self, port: u16) -> Self {
455        self.start_port = Some(port);
456        self
457    }
458
459    /// Set whether to start the iFlow process in debug mode
460    pub fn debug(mut self, debug: bool) -> Self {
461        self.debug = debug;
462        self
463    }
464
465    /// Disable process auto-start
466    pub fn manual_start(self) -> Self {
467        self.auto_start(false)
468    }
469
470    /// Enable process auto-start
471    pub fn enable_auto_start(self) -> Self {
472        self.auto_start(true)
473    }
474
475    /// Enable debug mode
476    pub fn enable_debug(self) -> Self {
477        self.debug(true)
478    }
479
480    /// Configure for stdio mode (no port needed)
481    pub fn stdio_mode(mut self) -> Self {
482        self.start_port = None;
483        self
484    }
485}
486
487/// Configuration for logging
488#[derive(Debug, Clone)]
489pub struct LoggingConfig {
490    /// Whether logging is enabled
491    pub enabled: bool,
492    /// Log level
493    pub level: String,
494    /// Logger configuration
495    pub logger_config: LoggerConfig,
496}
497
498impl Default for LoggingConfig {
499    fn default() -> Self {
500        Self {
501            enabled: false,
502            level: "INFO".to_string(),
503            logger_config: LoggerConfig::default(),
504        }
505    }
506}
507
508/// Configuration options for iFlow SDK
509///
510/// This struct contains all the configuration options for the iFlow SDK,
511/// including connection settings, security options, and logging configuration.
512#[derive(Debug, Clone)]
513pub struct IFlowOptions {
514    /// Current working directory
515    pub cwd: PathBuf,
516    /// MCP servers to connect to
517    pub mcp_servers: Vec<McpServer>,
518    /// Request timeout in seconds
519    pub timeout: f64,
520    /// Additional metadata to include in requests
521    pub metadata: HashMap<String, serde_json::Value>,
522    /// File access configuration
523    pub file_access: FileAccessConfig,
524    /// Process management configuration
525    pub process: ProcessConfig,
526    /// Authentication method ID
527    pub auth_method_id: Option<String>,
528    /// Logging configuration
529    pub logging: LoggingConfig,
530    /// WebSocket configuration (if None, use stdio)
531    pub websocket: Option<WebSocketConfig>,
532    /// Permission mode for tool calls
533    pub permission_mode: PermissionMode,
534}
535
536impl Default for IFlowOptions {
537    fn default() -> Self {
538        Self {
539            cwd: std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")),
540            mcp_servers: Vec::new(),
541            timeout: 120.0,
542            metadata: HashMap::new(),
543            file_access: FileAccessConfig::default(),
544            process: ProcessConfig::default(),
545            auth_method_id: None,
546            logging: LoggingConfig::default(),
547            websocket: None,
548            permission_mode: PermissionMode::Auto,
549        }
550    }
551}
552
553impl IFlowOptions {
554    /// Create a new IFlowOptions instance with default values
555    pub fn new() -> Self {
556        Self::default()
557    }
558
559    /// Set the current working directory
560    ///
561    /// # Arguments
562    /// * `cwd` - The current working directory
563    pub fn with_cwd(mut self, cwd: PathBuf) -> Self {
564        self.cwd = cwd;
565        self
566    }
567
568    /// Set the request timeout
569    ///
570    /// # Arguments
571    /// * `timeout` - Timeout in seconds
572    pub fn with_timeout(mut self, timeout: f64) -> Self {
573        self.timeout = timeout;
574        self
575    }
576
577    /// Set MCP servers to connect to
578    ///
579    /// # Arguments
580    /// * `servers` - The MCP servers to connect to
581    pub fn with_mcp_servers(mut self, servers: Vec<McpServer>) -> Self {
582        self.mcp_servers = servers;
583        self
584    }
585
586    /// Set additional metadata to include in requests
587    ///
588    /// # Arguments
589    /// * `metadata` - The metadata to include
590    pub fn with_metadata(mut self, metadata: HashMap<String, serde_json::Value>) -> Self {
591        self.metadata = metadata;
592        self
593    }
594
595    /// Set file access configuration
596    ///
597    /// # Arguments
598    /// * `config` - The file access configuration
599    pub fn with_file_access_config(mut self, config: FileAccessConfig) -> Self {
600        self.file_access = config;
601        self
602    }
603
604    /// Set process management configuration
605    ///
606    /// # Arguments
607    /// * `config` - The process management configuration
608    pub fn with_process_config(mut self, config: ProcessConfig) -> Self {
609        self.process = config;
610        self
611    }
612
613    /// Set auto start process
614    ///
615    /// # Arguments
616    /// * `auto_start` - Whether to automatically start the iFlow process
617    pub fn with_auto_start(mut self, auto_start: bool) -> Self {
618        self.process.auto_start = auto_start;
619        self
620    }
621
622    /// Set authentication method ID
623    ///
624    /// # Arguments
625    /// * `method_id` - The authentication method ID
626    pub fn with_auth_method_id(mut self, method_id: String) -> Self {
627        self.auth_method_id = Some(method_id);
628        self
629    }
630
631    /// Set logging configuration
632    ///
633    /// # Arguments
634    /// * `config` - The logging configuration
635    pub fn with_logging_config(mut self, config: LoggingConfig) -> Self {
636        self.logging = config;
637        self
638    }
639
640    /// Set WebSocket configuration
641    ///
642    /// # Arguments
643    /// * `config` - The WebSocket configuration
644    pub fn with_websocket_config(mut self, config: WebSocketConfig) -> Self {
645        self.websocket = Some(config);
646        self
647    }
648
649    /// Set permission mode for tool calls
650    ///
651    /// # Arguments
652    /// * `mode` - The permission mode to use
653    pub fn with_permission_mode(mut self, mode: PermissionMode) -> Self {
654        self.permission_mode = mode;
655        self
656    }
657}
658
659/// Error message details
660#[derive(Debug, Clone, Serialize, Deserialize)]
661pub struct ErrorMessageDetails {
662    /// Error code
663    pub code: i32,
664    /// Error message
665    pub message: String,
666    /// Optional error details
667    #[serde(skip_serializing_if = "Option::is_none")]
668    pub details: Option<std::collections::HashMap<String, serde_json::Value>>,
669}
670
671impl ErrorMessageDetails {
672    /// Create a new error message details
673    ///
674    /// # Arguments
675    /// * `code` - The error code
676    /// * `message` - The error message
677    ///
678    /// # Returns
679    /// A new ErrorMessageDetails instance
680    pub fn new(code: i32, message: String) -> Self {
681        Self {
682            code,
683            message,
684            details: None,
685        }
686    }
687
688    /// Create a new error message details with details
689    ///
690    /// # Arguments
691    /// * `code` - The error code
692    /// * `message` - The error message
693    /// * `details` - Additional error details
694    ///
695    /// # Returns
696    /// A new ErrorMessageDetails instance
697    pub fn with_details(
698        code: i32,
699        message: String,
700        details: std::collections::HashMap<String, serde_json::Value>,
701    ) -> Self {
702        Self {
703            code,
704            message,
705            details: Some(details),
706        }
707    }
708}
709
710/// Message types for communication with iFlow
711///
712/// These are the various message types that can be exchanged with iFlow
713/// during a session.
714#[derive(Debug, Clone, Serialize, Deserialize)]
715#[serde(tag = "type")]
716pub enum Message {
717    /// User message
718    #[serde(rename = "user")]
719    User { content: String },
720
721    /// Assistant message
722    #[serde(rename = "assistant")]
723    Assistant { content: String },
724
725    /// Tool call message
726    #[serde(rename = "tool_call")]
727    ToolCall {
728        id: String,
729        name: String,
730        status: String,
731    },
732
733    /// Plan message
734    #[serde(rename = "plan")]
735    Plan { entries: Vec<PlanEntry> },
736
737    /// Task finish message
738    #[serde(rename = "task_finish")]
739    TaskFinish { reason: Option<String> },
740
741    /// Error message
742    #[serde(rename = "error")]
743    Error {
744        code: i32,
745        message: String,
746        #[serde(skip_serializing_if = "Option::is_none")]
747        details: Option<std::collections::HashMap<String, serde_json::Value>>,
748    },
749}
750
751impl Message {
752    /// Check if this is a task finish message
753    pub fn is_task_finish(&self) -> bool {
754        matches!(self, Message::TaskFinish { .. })
755    }
756
757    /// Check if this is an error message
758    pub fn is_error(&self) -> bool {
759        matches!(self, Message::Error { .. })
760    }
761
762    /// Get the text content of the message if it has any
763    ///
764    /// # Returns
765    /// `Some(&str)` containing the text content if the message has text content,
766    /// `None` otherwise
767    pub fn get_text(&self) -> Option<&str> {
768        match self {
769            Message::User { content } => Some(content),
770            Message::Assistant { content } => Some(content),
771            _ => None,
772        }
773    }
774
775    /// Create a new error message
776    ///
777    /// # Arguments
778    /// * `code` - The error code
779    /// * `message` - The error message
780    ///
781    /// # Returns
782    /// A new Message::Error variant
783    pub fn error(code: i32, message: String) -> Self {
784        Message::Error {
785            code,
786            message,
787            details: None,
788        }
789    }
790
791    /// Create a new error message with details
792    ///
793    /// # Arguments
794    /// * `code` - The error code
795    /// * `message` - The error message
796    /// * `details` - Additional error details
797    ///
798    /// # Returns
799    /// A new Message::Error variant
800    pub fn error_with_details(
801        code: i32,
802        message: String,
803        details: std::collections::HashMap<String, serde_json::Value>,
804    ) -> Self {
805        Message::Error {
806            code,
807            message,
808            details: Some(details),
809        }
810    }
811}