use std::path::Path;use codex_analytics::GuardianReviewedAction;use codex_protocol::approvals::GuardianAssessmentAction;use codex_protocol::approvals::GuardianCommandSource;use codex_protocol::approvals::NetworkApprovalProtocol;use codex_protocol::models::AdditionalPermissionProfile;use codex_protocol::request_permissions::RequestPermissionProfile;use codex_utils_absolute_path::AbsolutePathBuf;use serde::Serialize;use serde_json::Value;use super::GUARDIAN_MAX_ACTION_STRING_TOKENS;use super::prompt::guardian_truncate_text;#[derive(Debug, Clone, PartialEq)]pub(crate) enum GuardianApprovalRequest { Shell { id: String, command: Vec<String>, cwd: AbsolutePathBuf, sandbox_permissions: crate::sandboxing::SandboxPermissions, additional_permissions: Option<AdditionalPermissionProfile>, justification: Option<String>, }, ExecCommand { id: String, command: Vec<String>, cwd: AbsolutePathBuf, sandbox_permissions: crate::sandboxing::SandboxPermissions, additional_permissions: Option<AdditionalPermissionProfile>, justification: Option<String>, tty: bool, }, #[cfg(unix)] Execve { id: String, source: GuardianCommandSource, program: String, argv: Vec<String>, cwd: AbsolutePathBuf, additional_permissions: Option<AdditionalPermissionProfile>, }, ApplyPatch { id: String, cwd: AbsolutePathBuf, files: Vec<AbsolutePathBuf>, patch: String, }, NetworkAccess { id: String, turn_id: String, target: String, host: String, protocol: NetworkApprovalProtocol, port: u16, trigger: Option<GuardianNetworkAccessTrigger>, }, McpToolCall { id: String, server: String, tool_name: String, arguments: Option<Value>, connector_id: Option<String>, connector_name: Option<String>, connector_description: Option<String>, tool_title: Option<String>, tool_description: Option<String>, annotations: Option<GuardianMcpAnnotations>, }, RequestPermissions { id: String, turn_id: String, reason: Option<String>, permissions: RequestPermissionProfile, },}#[derive(Debug, Clone, PartialEq, Serialize)]#[serde(rename_all = "camelCase")]pub(crate) struct GuardianNetworkAccessTrigger { pub(crate) call_id: String, pub(crate) tool_name: String, pub(crate) command: Vec<String>, pub(crate) cwd: AbsolutePathBuf, pub(crate) sandbox_permissions: crate::sandboxing::SandboxPermissions, #[serde(skip_serializing_if = "Option::is_none")] pub(crate) additional_permissions: Option<AdditionalPermissionProfile>, #[serde(skip_serializing_if = "Option::is_none")] pub(crate) justification: Option<String>, #[serde(skip_serializing_if = "Option::is_none")] pub(crate) tty: Option<bool>,}#[derive(Debug, Clone, PartialEq, Eq, Serialize)]pub(crate) struct GuardianMcpAnnotations { #[serde(skip_serializing_if = "Option::is_none")] pub(crate) destructive_hint: Option<bool>, #[serde(skip_serializing_if = "Option::is_none")] pub(crate) open_world_hint: Option<bool>, #[serde(skip_serializing_if = "Option::is_none")] pub(crate) read_only_hint: Option<bool>,}#[derive(Serialize)]struct CommandApprovalAction<'a> { tool: &'a str, command: &'a [String], cwd: &'a Path, sandbox_permissions: crate::sandboxing::SandboxPermissions, #[serde(skip_serializing_if = "Option::is_none")] additional_permissions: Option<&'a AdditionalPermissionProfile>, #[serde(skip_serializing_if = "Option::is_none")] justification: Option<&'a String>, #[serde(skip_serializing_if = "Option::is_none")] tty: Option<bool>,}#[cfg(unix)]#[derive(Serialize)]struct ExecveApprovalAction<'a> { tool: &'a str, program: &'a str, argv: &'a [String], cwd: &'a Path, #[serde(skip_serializing_if = "Option::is_none")] additional_permissions: Option<&'a AdditionalPermissionProfile>,}#[derive(Serialize)]struct McpToolCallApprovalAction<'a> { tool: &'static str, server: &'a str, tool_name: &'a str, #[serde(skip_serializing_if = "Option::is_none")] arguments: Option<&'a Value>, #[serde(skip_serializing_if = "Option::is_none")] connector_id: Option<&'a String>, #[serde(skip_serializing_if = "Option::is_none")] connector_name: Option<&'a String>, #[serde(skip_serializing_if = "Option::is_none")] connector_description: Option<&'a String>, #[serde(skip_serializing_if = "Option::is_none")] tool_title: Option<&'a String>, #[serde(skip_serializing_if = "Option::is_none")] tool_description: Option<&'a String>, #[serde(skip_serializing_if = "Option::is_none")] annotations: Option<&'a GuardianMcpAnnotations>,}#[derive(Serialize)]#[serde(rename_all = "camelCase")]struct NetworkAccessApprovalAction<'a> { tool: &'static str, target: &'a str, host: &'a str, protocol: NetworkApprovalProtocol, port: u16, #[serde(skip_serializing_if = "Option::is_none")] trigger: Option<&'a GuardianNetworkAccessTrigger>,}#[derive(Serialize)]struct RequestPermissionsApprovalAction<'a> { tool: &'static str, turn_id: &'a str, #[serde(skip_serializing_if = "Option::is_none")] reason: Option<&'a String>, permissions: &'a RequestPermissionProfile,}fn serialize_guardian_action(value: impl Serialize) -> serde_json::Result<Value> { serde_json::to_value(value)}fn serialize_command_guardian_action( tool: &'static str, command: &[String], cwd: &Path, sandbox_permissions: crate::sandboxing::SandboxPermissions, additional_permissions: Option<&AdditionalPermissionProfile>, justification: Option<&String>, tty: Option<bool>,) -> serde_json::Result<Value> { serialize_guardian_action(CommandApprovalAction { tool, command, cwd, sandbox_permissions, additional_permissions, justification, tty, })}fn command_assessment_action( source: GuardianCommandSource, command: &[String], cwd: &AbsolutePathBuf,) -> GuardianAssessmentAction { GuardianAssessmentAction::Command { source, command: codex_shell_command::parse_command::shlex_join(command), cwd: cwd.clone(), }}#[cfg(unix)]fn guardian_command_source_tool_name(source: GuardianCommandSource) -> &'static str { match source { GuardianCommandSource::Shell => "shell", GuardianCommandSource::UnifiedExec => "exec_command", }}fn truncate_guardian_action_value(value: Value) -> (Value, bool) { match value { Value::String(text) => { let (text, truncated) = guardian_truncate_text(&text, GUARDIAN_MAX_ACTION_STRING_TOKENS); (Value::String(text), truncated) } Value::Array(values) => { let mut truncated = false; let values = values .into_iter() .map(|value| { let (value, value_truncated) = truncate_guardian_action_value(value); truncated |= value_truncated; value }) .collect::<Vec<_>>(); (Value::Array(values), truncated) } Value::Object(values) => { let mut entries = values.into_iter().collect::<Vec<_>>(); entries.sort_by(|(left, _), (right, _)| left.cmp(right)); let mut truncated = false; let values = entries .into_iter() .map(|(key, value)| { let (value, value_truncated) = truncate_guardian_action_value(value); truncated |= value_truncated; (key, value) }) .collect(); (Value::Object(values), truncated) } other => (other, false), }}#[derive(Debug, Clone, PartialEq, Eq)]pub(crate) struct FormattedGuardianAction { pub(crate) text: String, pub(crate) truncated: bool,}pub(crate) fn guardian_approval_request_to_json( action: &GuardianApprovalRequest,) -> serde_json::Result<Value> { match action { GuardianApprovalRequest::Shell { id: _, command, cwd, sandbox_permissions, additional_permissions, justification, } => serialize_command_guardian_action( "shell", command, cwd, *sandbox_permissions, additional_permissions.as_ref(), justification.as_ref(), /*tty*/ None, ), GuardianApprovalRequest::ExecCommand { id: _, command, cwd, sandbox_permissions, additional_permissions, justification, tty, } => serialize_command_guardian_action( "exec_command", command, cwd, *sandbox_permissions, additional_permissions.as_ref(), justification.as_ref(), Some(*tty), ), #[cfg(unix)] GuardianApprovalRequest::Execve { id: _, source, program, argv, cwd, additional_permissions, } => serialize_guardian_action(ExecveApprovalAction { tool: guardian_command_source_tool_name(*source), program, argv, cwd, additional_permissions: additional_permissions.as_ref(), }), GuardianApprovalRequest::ApplyPatch { id: _, cwd, files, patch, } => Ok(serde_json::json!({ "tool": "apply_patch", "cwd": cwd, "files": files, "patch": patch, })), GuardianApprovalRequest::NetworkAccess { id: _, turn_id: _, target, host, protocol, port, trigger, } => serialize_guardian_action(NetworkAccessApprovalAction { tool: "network_access", target, host, protocol: *protocol, port: *port, trigger: trigger.as_ref(), }), GuardianApprovalRequest::McpToolCall { id: _, server, tool_name, arguments, connector_id, connector_name, connector_description, tool_title, tool_description, annotations, } => serialize_guardian_action(McpToolCallApprovalAction { tool: "mcp_tool_call", server, tool_name, arguments: arguments.as_ref(), connector_id: connector_id.as_ref(), connector_name: connector_name.as_ref(), connector_description: connector_description.as_ref(), tool_title: tool_title.as_ref(), tool_description: tool_description.as_ref(), annotations: annotations.as_ref(), }), GuardianApprovalRequest::RequestPermissions { id: _, turn_id, reason, permissions, } => serialize_guardian_action(RequestPermissionsApprovalAction { tool: "request_permissions", turn_id, reason: reason.as_ref(), permissions, }), }}pub(crate) fn guardian_assessment_action( action: &GuardianApprovalRequest,) -> GuardianAssessmentAction { match action { GuardianApprovalRequest::Shell { command, cwd, .. } => { command_assessment_action(GuardianCommandSource::Shell, command, cwd) } GuardianApprovalRequest::ExecCommand { command, cwd, .. } => { command_assessment_action(GuardianCommandSource::UnifiedExec, command, cwd) } #[cfg(unix)] GuardianApprovalRequest::Execve { source, program, argv, cwd, .. } => GuardianAssessmentAction::Execve { source: *source, program: program.clone(), argv: argv.clone(), cwd: cwd.clone(), }, GuardianApprovalRequest::ApplyPatch { cwd, files, .. } => { GuardianAssessmentAction::ApplyPatch { cwd: cwd.clone(), files: files.clone(), } } GuardianApprovalRequest::NetworkAccess { id: _id, turn_id: _turn_id, target, host, protocol, port, trigger: _trigger, } => GuardianAssessmentAction::NetworkAccess { target: target.clone(), host: host.clone(), protocol: *protocol, port: *port, }, GuardianApprovalRequest::McpToolCall { server, tool_name, connector_id, connector_name, tool_title, .. } => GuardianAssessmentAction::McpToolCall { server: server.clone(), tool_name: tool_name.clone(), connector_id: connector_id.clone(), connector_name: connector_name.clone(), tool_title: tool_title.clone(), }, GuardianApprovalRequest::RequestPermissions { reason, permissions, .. } => GuardianAssessmentAction::RequestPermissions { reason: reason.clone(), permissions: permissions.clone(), }, }}pub(crate) fn guardian_reviewed_action( request: &GuardianApprovalRequest,) -> GuardianReviewedAction { match request { GuardianApprovalRequest::Shell { sandbox_permissions, additional_permissions, .. } => GuardianReviewedAction::Shell { sandbox_permissions: *sandbox_permissions, additional_permissions: additional_permissions.clone(), }, GuardianApprovalRequest::ExecCommand { sandbox_permissions, additional_permissions, tty, .. } => GuardianReviewedAction::UnifiedExec { sandbox_permissions: *sandbox_permissions, additional_permissions: additional_permissions.clone(), tty: *tty, }, #[cfg(unix)] GuardianApprovalRequest::Execve { source, program, additional_permissions, .. } => GuardianReviewedAction::Execve { source: *source, program: program.clone(), additional_permissions: additional_permissions.clone(), }, GuardianApprovalRequest::ApplyPatch { .. } => GuardianReviewedAction::ApplyPatch {}, GuardianApprovalRequest::NetworkAccess { protocol, port, .. } => { GuardianReviewedAction::NetworkAccess { protocol: *protocol, port: *port, } } GuardianApprovalRequest::McpToolCall { server, tool_name, connector_id, connector_name, tool_title, .. } => GuardianReviewedAction::McpToolCall { server: server.clone(), tool_name: tool_name.clone(), connector_id: connector_id.clone(), connector_name: connector_name.clone(), tool_title: tool_title.clone(), }, GuardianApprovalRequest::RequestPermissions { .. } => { GuardianReviewedAction::RequestPermissions {} } }}pub(crate) fn guardian_request_target_item_id(request: &GuardianApprovalRequest) -> Option<&str> { match request { GuardianApprovalRequest::Shell { id, .. } | GuardianApprovalRequest::ExecCommand { id, .. } | GuardianApprovalRequest::ApplyPatch { id, .. } | GuardianApprovalRequest::McpToolCall { id, .. } | GuardianApprovalRequest::RequestPermissions { id, .. } => Some(id), GuardianApprovalRequest::NetworkAccess { .. } => None, #[cfg(unix)] GuardianApprovalRequest::Execve { id, .. } => Some(id), }}pub(crate) fn guardian_request_turn_id<'a>( request: &'a GuardianApprovalRequest, default_turn_id: &'a str,) -> &'a str { match request { GuardianApprovalRequest::NetworkAccess { turn_id, .. } | GuardianApprovalRequest::RequestPermissions { turn_id, .. } => turn_id, GuardianApprovalRequest::Shell { .. } | GuardianApprovalRequest::ExecCommand { .. } | GuardianApprovalRequest::ApplyPatch { .. } | GuardianApprovalRequest::McpToolCall { .. } => default_turn_id, #[cfg(unix)] GuardianApprovalRequest::Execve { .. } => default_turn_id, }}pub(crate) fn format_guardian_action_pretty( action: &GuardianApprovalRequest,) -> serde_json::Result<FormattedGuardianAction> { let value = guardian_approval_request_to_json(action)?; let (value, truncated) = truncate_guardian_action_value(value); Ok(FormattedGuardianAction { text: serde_json::to_string_pretty(&value)?, truncated, })}