//! Operating modes for Owlen //! //! Defines the different modes in which Owlen can operate and their associated //! tool availability policies. use serde::{Deserialize, Serialize}; use std::str::FromStr; /// Operating mode for Owlen #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)] #[serde(rename_all = "lowercase")] pub enum Mode { /// Chat mode - limited tool access, safe for general conversation #[default] Chat, /// Code mode - full tool access for development tasks Code, } impl Mode { /// Get the display name for this mode pub fn display_name(&self) -> &'static str { match self { Mode::Chat => "chat", Mode::Code => "code", } } } impl std::fmt::Display for Mode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.display_name()) } } impl FromStr for Mode { type Err = String; fn from_str(s: &str) -> Result { match s.to_lowercase().as_str() { "chat" => Ok(Mode::Chat), "code" => Ok(Mode::Code), _ => Err(format!( "Invalid mode: '{}'. Valid modes are 'chat' or 'code'", s )), } } } /// Configuration for tool availability in different modes #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ModeConfig { /// Tools allowed in chat mode #[serde(default = "ModeConfig::default_chat_tools")] pub chat: ModeToolConfig, /// Tools allowed in code mode #[serde(default = "ModeConfig::default_code_tools")] pub code: ModeToolConfig, } impl Default for ModeConfig { fn default() -> Self { Self { chat: Self::default_chat_tools(), code: Self::default_code_tools(), } } } impl ModeConfig { fn default_chat_tools() -> ModeToolConfig { ModeToolConfig { allowed_tools: vec!["web_search".to_string()], } } fn default_code_tools() -> ModeToolConfig { ModeToolConfig { allowed_tools: vec!["*".to_string()], // All tools allowed } } /// Check if a tool is allowed in the given mode pub fn is_tool_allowed(&self, mode: Mode, tool_name: &str) -> bool { let config = match mode { Mode::Chat => &self.chat, Mode::Code => &self.code, }; config.is_tool_allowed(tool_name) } } /// Tool configuration for a specific mode #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ModeToolConfig { /// List of allowed tools. Use "*" to allow all tools. pub allowed_tools: Vec, } impl ModeToolConfig { /// Check if a tool is allowed in this mode pub fn is_tool_allowed(&self, tool_name: &str) -> bool { // Check for wildcard if self.allowed_tools.iter().any(|t| t == "*") { return true; } // Check if tool is explicitly listed self.allowed_tools.iter().any(|t| t == tool_name) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_mode_display() { assert_eq!(Mode::Chat.to_string(), "chat"); assert_eq!(Mode::Code.to_string(), "code"); } #[test] fn test_mode_from_str() { assert_eq!("chat".parse::(), Ok(Mode::Chat)); assert_eq!("code".parse::(), Ok(Mode::Code)); assert_eq!("CHAT".parse::(), Ok(Mode::Chat)); assert_eq!("CODE".parse::(), Ok(Mode::Code)); assert!("invalid".parse::().is_err()); } #[test] fn test_default_mode() { assert_eq!(Mode::default(), Mode::Chat); } #[test] fn test_chat_mode_restrictions() { let config = ModeConfig::default(); // Web search should be allowed in chat mode assert!(config.is_tool_allowed(Mode::Chat, "web_search")); // Code exec should not be allowed in chat mode assert!(!config.is_tool_allowed(Mode::Chat, "code_exec")); assert!(!config.is_tool_allowed(Mode::Chat, "file_write")); } #[test] fn test_code_mode_allows_all() { let config = ModeConfig::default(); // All tools should be allowed in code mode assert!(config.is_tool_allowed(Mode::Code, "web_search")); assert!(config.is_tool_allowed(Mode::Code, "code_exec")); assert!(config.is_tool_allowed(Mode::Code, "file_write")); assert!(config.is_tool_allowed(Mode::Code, "anything")); } #[test] fn test_wildcard_tool_config() { let config = ModeToolConfig { allowed_tools: vec!["*".to_string()], }; assert!(config.is_tool_allowed("any_tool")); assert!(config.is_tool_allowed("another_tool")); } #[test] fn test_explicit_tool_list() { let config = ModeToolConfig { allowed_tools: vec!["tool1".to_string(), "tool2".to_string()], }; assert!(config.is_tool_allowed("tool1")); assert!(config.is_tool_allowed("tool2")); assert!(!config.is_tool_allowed("tool3")); } }