use codex_analytics::GuardianReviewedAction; pub(crate) use codex_guardian_reviewer::GuardianApprovalRequest; pub(crate) use codex_guardian_reviewer::GuardianMcpAnnotations; pub(crate) use codex_guardian_reviewer::GuardianNetworkAccessTrigger; 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_path_uri::LegacyAppPathString; use serde::Serialize; use serde_json::Value; #[derive(Serialize)] struct CommandApprovalAction<'a> { tool: &'a str, command: &'a [String], cwd: LegacyAppPathString, 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, } #[derive(Serialize)] struct ApplyPatchApprovalAction<'a> { tool: &'static str, cwd: LegacyAppPathString, files: Vec, patch: &'a str, } #[derive(Serialize)] struct WriteStdinApprovalAction<'a> { tool: &'static str, session_id: i32, chars: &'a str, cwd: LegacyAppPathString, sandbox_permissions: crate::sandboxing::SandboxPermissions, #[serde(skip_serializing_if = "Option::is_none")] additional_permissions: Option<&'a AdditionalPermissionProfile>, tty: bool, } #[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")] connected_account_email: 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 { serde_json::to_value(value) } fn serialize_command_guardian_action( tool: &'static str, command: &[String], cwd: LegacyAppPathString, sandbox_permissions: crate::sandboxing::SandboxPermissions, additional_permissions: Option<&AdditionalPermissionProfile>, justification: Option<&String>, tty: Option, ) -> serde_json::Result { serialize_guardian_action(CommandApprovalAction { tool, command, cwd, sandbox_permissions, additional_permissions, justification, tty, }) } fn command_assessment_action( source: GuardianCommandSource, command: &[String], cwd: &LegacyAppPathString, ) -> GuardianAssessmentAction { GuardianAssessmentAction::Command { source, command: codex_shell_command::parse_command::shlex_join(command), cwd: cwd.clone(), } } pub(crate) fn guardian_approval_request_to_json( action: &GuardianApprovalRequest, ) -> serde_json::Result { let mut value = match action { GuardianApprovalRequest::NativeToolCall { call, .. } => serde_json::to_value(call.as_ref()), GuardianApprovalRequest::ExecCommand { id: _, environment_id: _, command, cwd: _, guardian_cwd, sandbox_permissions, additional_permissions, justification, tty, } => serialize_command_guardian_action( "exec_command", command, guardian_cwd.clone(), *sandbox_permissions, additional_permissions.as_ref(), justification.as_ref(), Some(*tty), ), GuardianApprovalRequest::WriteStdin { process_id, input, cwd, tty, sandbox_permissions, additional_permissions, .. } => serialize_guardian_action(WriteStdinApprovalAction { tool: "write_stdin", session_id: *process_id, chars: input, cwd: cwd.clone().into(), sandbox_permissions: *sandbox_permissions, additional_permissions: additional_permissions.as_ref(), tty: *tty, }), GuardianApprovalRequest::ApplyPatch { id: _, environment_id: _, cwd, files, patch, } => serialize_guardian_action(ApplyPatchApprovalAction { tool: "apply_patch", cwd: cwd.clone().into(), files: files.iter().cloned().map(Into::into).collect(), patch, }), GuardianApprovalRequest::NetworkAccess { id: _, turn_id: _, environment_id: _, target, host, protocol, port, trigger, } => { let mut value = serialize_guardian_action(NetworkAccessApprovalAction { tool: "network_access", target, host, protocol: *protocol, port: *port, trigger: trigger.as_ref(), })?; if let Some(trigger) = trigger { value["trigger"]["cwd"] = trigger.cwd.inferred_native_path_string().into(); } Ok(value) } GuardianApprovalRequest::McpToolCall { id: _, server, tool_name, arguments, connector_id, connector_name, connector_description, connected_account_email, 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(), connected_account_email: connected_account_email.as_ref(), tool_title: tool_title.as_ref(), tool_description: tool_description.as_ref(), annotations: annotations.as_ref(), }), GuardianApprovalRequest::RequestPermissions { id: _, environment_id: _, turn_id, reason, permissions, } => serialize_guardian_action(RequestPermissionsApprovalAction { tool: "request_permissions", turn_id, reason: reason.as_ref(), permissions, }), }?; if let Some(environment_id) = action.target_environment_id() { value["environment_id"] = environment_id.into(); } Ok(value) } pub(crate) fn guardian_assessment_action( action: &GuardianApprovalRequest, ) -> Option { Some(match action { GuardianApprovalRequest::NativeToolCall { .. } => return None, GuardianApprovalRequest::ExecCommand { command, guardian_cwd, .. } => command_assessment_action(GuardianCommandSource::UnifiedExec, command, guardian_cwd), GuardianApprovalRequest::WriteStdin { approval_id, process_id, input, cwd, .. } => GuardianAssessmentAction::WriteStdin { approval_id: approval_id.clone(), process_id: process_id.to_string(), stdin: input.clone(), cwd: cwd.clone(), }, GuardianApprovalRequest::ApplyPatch { cwd, files, .. } => { GuardianAssessmentAction::ApplyPatch { cwd: cwd.clone().into(), files: files.iter().cloned().map(Into::into).collect(), } } GuardianApprovalRequest::NetworkAccess { id: _id, turn_id: _turn_id, environment_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::NativeToolCall { .. } => GuardianReviewedAction::NativeToolCall {}, GuardianApprovalRequest::ExecCommand { sandbox_permissions, additional_permissions, tty, .. } => GuardianReviewedAction::UnifiedExec { sandbox_permissions: *sandbox_permissions, additional_permissions: additional_permissions.as_ref().map(Into::into), tty: *tty, }, GuardianApprovalRequest::WriteStdin { tty, .. } => { GuardianReviewedAction::WriteStdin { tty: *tty } } 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::ExecCommand { id, .. } | GuardianApprovalRequest::WriteStdin { id, .. } | GuardianApprovalRequest::ApplyPatch { id, .. } | GuardianApprovalRequest::McpToolCall { id, .. } | GuardianApprovalRequest::RequestPermissions { id, .. } | GuardianApprovalRequest::NativeToolCall { id, .. } => Some(id), GuardianApprovalRequest::NetworkAccess { .. } => None, } } 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::ExecCommand { .. } | GuardianApprovalRequest::WriteStdin { .. } | GuardianApprovalRequest::ApplyPatch { .. } | GuardianApprovalRequest::McpToolCall { .. } | GuardianApprovalRequest::NativeToolCall { .. } => default_turn_id, } } pub(crate) fn format_guardian_action_pretty( action: &GuardianApprovalRequest, ) -> serde_json::Result { let mut value = codex_guardian_context::action_for_review(guardian_approval_request_to_json(action)?); value.sort_all_objects(); serde_json::to_string_pretty(&value) }