Skip to main content

codex_codes/io/
options.rs

1use serde::{Deserialize, Serialize};
2
3/// Approval mode for tool execution.
4#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
5#[serde(rename_all = "kebab-case")]
6pub enum ApprovalMode {
7    Never,
8    OnRequest,
9    OnFailure,
10    Untrusted,
11}
12
13/// Sandbox mode controlling file system access.
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(rename_all = "kebab-case")]
16pub enum SandboxMode {
17    ReadOnly,
18    WorkspaceWrite,
19    DangerFullAccess,
20}
21
22impl SandboxMode {
23    /// Get the CLI flag value for this sandbox mode.
24    pub fn as_cli_str(&self) -> &'static str {
25        match self {
26            SandboxMode::ReadOnly => "read-only",
27            SandboxMode::WorkspaceWrite => "workspace-write",
28            SandboxMode::DangerFullAccess => "danger-full-access",
29        }
30    }
31}
32
33/// Model reasoning effort level.
34#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
35#[serde(rename_all = "snake_case")]
36pub enum ModelReasoningEffort {
37    Minimal,
38    Low,
39    Medium,
40    High,
41    Xhigh,
42}
43
44/// Web search mode.
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
46#[serde(rename_all = "snake_case")]
47pub enum WebSearchMode {
48    Disabled,
49    Cached,
50    Live,
51}
52
53/// Per-thread options controlling model, sandbox, and behavior.
54#[derive(Debug, Clone, Default, Serialize, Deserialize)]
55pub struct ThreadOptions {
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub model: Option<String>,
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub sandbox_mode: Option<SandboxMode>,
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub working_directory: Option<String>,
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub skip_git_repo_check: Option<bool>,
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub model_reasoning_effort: Option<ModelReasoningEffort>,
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub network_access_enabled: Option<bool>,
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub web_search_mode: Option<WebSearchMode>,
70    #[serde(skip_serializing_if = "Option::is_none")]
71    pub web_search_enabled: Option<bool>,
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub approval_policy: Option<ApprovalMode>,
74    #[serde(default, skip_serializing_if = "Vec::is_empty")]
75    pub additional_directories: Vec<String>,
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn test_thread_options_default() {
84        let opts = ThreadOptions::default();
85        assert!(opts.model.is_none());
86        assert!(opts.sandbox_mode.is_none());
87        assert!(opts.additional_directories.is_empty());
88    }
89
90    #[test]
91    fn test_approval_mode_serde() {
92        let json = r#""on-request""#;
93        let mode: ApprovalMode = serde_json::from_str(json).unwrap();
94        assert_eq!(mode, ApprovalMode::OnRequest);
95    }
96
97    #[test]
98    fn test_sandbox_mode_serde() {
99        let json = r#""workspace-write""#;
100        let mode: SandboxMode = serde_json::from_str(json).unwrap();
101        assert_eq!(mode, SandboxMode::WorkspaceWrite);
102    }
103
104    #[test]
105    fn test_reasoning_effort_serde() {
106        let json = r#""xhigh""#;
107        let effort: ModelReasoningEffort = serde_json::from_str(json).unwrap();
108        assert_eq!(effort, ModelReasoningEffort::Xhigh);
109    }
110
111    #[test]
112    fn test_web_search_mode_serde() {
113        let json = r#""live""#;
114        let mode: WebSearchMode = serde_json::from_str(json).unwrap();
115        assert_eq!(mode, WebSearchMode::Live);
116    }
117}