mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
392 lines
12 KiB
Rust
392 lines
12 KiB
Rust
use std::path::Path;
|
|
|
|
use codex_protocol::approvals::GuardianAssessmentAction;
|
|
use codex_protocol::approvals::GuardianCommandSource;
|
|
use codex_protocol::approvals::NetworkApprovalProtocol;
|
|
use codex_protocol::models::PermissionProfile;
|
|
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<PermissionProfile>,
|
|
justification: Option<String>,
|
|
},
|
|
ExecCommand {
|
|
id: String,
|
|
command: Vec<String>,
|
|
cwd: AbsolutePathBuf,
|
|
sandbox_permissions: crate::sandboxing::SandboxPermissions,
|
|
additional_permissions: Option<PermissionProfile>,
|
|
justification: Option<String>,
|
|
tty: bool,
|
|
},
|
|
#[cfg(unix)]
|
|
Execve {
|
|
id: String,
|
|
source: GuardianCommandSource,
|
|
program: String,
|
|
argv: Vec<String>,
|
|
cwd: AbsolutePathBuf,
|
|
additional_permissions: Option<PermissionProfile>,
|
|
},
|
|
ApplyPatch {
|
|
id: String,
|
|
cwd: AbsolutePathBuf,
|
|
files: Vec<AbsolutePathBuf>,
|
|
patch: String,
|
|
},
|
|
NetworkAccess {
|
|
id: String,
|
|
turn_id: String,
|
|
target: String,
|
|
host: String,
|
|
protocol: NetworkApprovalProtocol,
|
|
port: u16,
|
|
},
|
|
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>,
|
|
},
|
|
}
|
|
|
|
#[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 PermissionProfile>,
|
|
#[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 PermissionProfile>,
|
|
}
|
|
|
|
#[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>,
|
|
}
|
|
|
|
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<&PermissionProfile>,
|
|
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 {
|
|
match value {
|
|
Value::String(text) => Value::String(guardian_truncate_text(
|
|
&text,
|
|
GUARDIAN_MAX_ACTION_STRING_TOKENS,
|
|
)),
|
|
Value::Array(values) => Value::Array(
|
|
values
|
|
.into_iter()
|
|
.map(truncate_guardian_action_value)
|
|
.collect::<Vec<_>>(),
|
|
),
|
|
Value::Object(values) => {
|
|
let mut entries = values.into_iter().collect::<Vec<_>>();
|
|
entries.sort_by(|(left, _), (right, _)| left.cmp(right));
|
|
Value::Object(
|
|
entries
|
|
.into_iter()
|
|
.map(|(key, value)| (key, truncate_guardian_action_value(value)))
|
|
.collect(),
|
|
)
|
|
}
|
|
other => other,
|
|
}
|
|
}
|
|
|
|
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,
|
|
} => Ok(serde_json::json!({
|
|
"tool": "network_access",
|
|
"target": target,
|
|
"host": host,
|
|
"protocol": protocol,
|
|
"port": port,
|
|
})),
|
|
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(),
|
|
}),
|
|
}
|
|
}
|
|
|
|
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: _,
|
|
turn_id: _,
|
|
target,
|
|
host,
|
|
protocol,
|
|
port,
|
|
} => 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(),
|
|
},
|
|
}
|
|
}
|
|
|
|
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, .. } => 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, .. } => 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<String> {
|
|
let mut value = guardian_approval_request_to_json(action)?;
|
|
value = truncate_guardian_action_value(value);
|
|
serde_json::to_string_pretty(&value)
|
|
}
|