use serde::Deserialize; use serde::Serialize; use strum_macros::Display as DeriveDisplay; use crate::protocol::AskForApproval; use crate::protocol::SandboxPolicy; use crate::shell::Shell; use codex_protocol::config_types::SandboxMode; use codex_protocol::models::ContentItem; use codex_protocol::models::ResponseItem; use codex_protocol::protocol::ENVIRONMENT_CONTEXT_CLOSE_TAG; use codex_protocol::protocol::ENVIRONMENT_CONTEXT_OPEN_TAG; use std::path::PathBuf; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, DeriveDisplay)] #[serde(rename_all = "kebab-case")] #[strum(serialize_all = "kebab-case")] pub enum NetworkAccess { Restricted, Enabled, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename = "environment_context", rename_all = "snake_case")] pub(crate) struct EnvironmentContext { pub cwd: Option, pub approval_policy: Option, pub sandbox_mode: Option, pub network_access: Option, pub shell: Option, } impl EnvironmentContext { pub fn new( cwd: Option, approval_policy: Option, sandbox_policy: Option, shell: Option, ) -> Self { Self { cwd, approval_policy, sandbox_mode: match sandbox_policy { Some(SandboxPolicy::DangerFullAccess) => Some(SandboxMode::DangerFullAccess), Some(SandboxPolicy::ReadOnly) => Some(SandboxMode::ReadOnly), Some(SandboxPolicy::WorkspaceWrite { .. }) => Some(SandboxMode::WorkspaceWrite), None => None, }, network_access: match sandbox_policy { Some(SandboxPolicy::DangerFullAccess) => Some(NetworkAccess::Enabled), Some(SandboxPolicy::ReadOnly) => Some(NetworkAccess::Restricted), Some(SandboxPolicy::WorkspaceWrite { network_access, .. }) => { if network_access { Some(NetworkAccess::Enabled) } else { Some(NetworkAccess::Restricted) } } None => None, }, shell, } } } impl EnvironmentContext { /// Serializes the environment context to XML. Libraries like `quick-xml` /// require custom macros to handle Enums with newtypes, so we just do it /// manually, to keep things simple. Output looks like: /// /// ```xml /// /// ... /// ... /// ... /// ... /// ... /// /// ``` pub fn serialize_to_xml(self) -> String { let mut lines = vec![ENVIRONMENT_CONTEXT_OPEN_TAG.to_string()]; if let Some(cwd) = self.cwd { lines.push(format!(" {}", cwd.to_string_lossy())); } if let Some(approval_policy) = self.approval_policy { lines.push(format!( " {approval_policy}" )); } if let Some(sandbox_mode) = self.sandbox_mode { lines.push(format!(" {sandbox_mode}")); } if let Some(network_access) = self.network_access { lines.push(format!( " {network_access}" )); } if let Some(shell) = self.shell && let Some(shell_name) = shell.name() { lines.push(format!(" {shell_name}")); } lines.push(ENVIRONMENT_CONTEXT_CLOSE_TAG.to_string()); lines.join("\n") } } impl From for ResponseItem { fn from(ec: EnvironmentContext) -> Self { ResponseItem::Message { id: None, role: "user".to_string(), content: vec![ContentItem::InputText { text: ec.serialize_to_xml(), }], } } }