File size: 3,146 Bytes
52a9af3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | //! Captures one approval action for the extension-owned synchronous reviewer.
use codex_protocol::protocol::ReviewDecision;
use std::sync::Arc;
use tokio_util::sync::CancellationToken;
use super::ApprovalRequestReasons;
use super::GuardianApprovalRequest;
use super::GuardianReviewContext;
use super::GuardianReviewOptions;
use super::approval_request::guardian_approval_request_to_json;
use crate::session::session::Session;
/// Carries the original action even when the legacy synchronous renderer cannot handle its paths.
/// This lets the extension return AskUser before invoking that renderer.
#[derive(Clone)]
pub(crate) struct ReviewAction {
pub(crate) action: Result<serde_json::Value, String>,
pub(crate) category: codex_protocol::openai_models::GuardianScope,
pub(crate) request: Result<GuardianApprovalRequest, String>,
}
impl From<GuardianApprovalRequest> for ReviewAction {
fn from(request: GuardianApprovalRequest) -> Self {
Self {
action: guardian_approval_request_to_json(&request).map_err(|error| error.to_string()),
category: request.guardian_scope(),
request: Ok(request),
}
}
}
impl ReviewAction {
pub(crate) fn from_approval_action(
action: crate::tools::sandboxing::ApprovalAction,
exec_command_cwd_convention: Option<codex_utils_path_uri::PathConvention>,
) -> Self {
let category = action.guardian_scope();
let original = serde_json::to_value(&action).map_err(|error| error.to_string());
match action.into_guardian_request(exec_command_cwd_convention) {
Ok(request) => Self::from(request),
Err(error) => Self {
action: original,
category,
request: Err(error.to_string()),
},
}
}
}
impl ReviewAction {
/// Preserve the checks previously made before entering Guardian from tool approvals.
pub(super) fn validate(
&self,
context: &GuardianReviewContext,
) -> Result<&GuardianApprovalRequest, ReviewDecision> {
let request = self.request.as_ref().map_err(|error| {
tracing::error!(%error, "failed to build automatic approval action");
ReviewDecision::denied("automatic approval review could not prepare the action")
})?;
if let GuardianApprovalRequest::WriteStdin { environment_id, .. } = request
&& !context
.environments()
.turn_environments()
.any(|environment| environment.selection.environment_id == *environment_id)
{
return Err(ReviewDecision::denied(
"automatic approval review cannot access the terminal's environment; select it before retrying",
));
}
Ok(request)
}
}
#[derive(Clone)]
pub(super) struct ReviewRuntime {
pub(super) session: Arc<Session>,
pub(super) history_reset: CancellationToken,
pub(super) context: GuardianReviewContext,
pub(super) request: ReviewAction,
pub(super) reasons: ApprovalRequestReasons,
pub(super) options: GuardianReviewOptions,
}
|