From 0d78fa8846b449558b7906dc052982fd71ecff97 Mon Sep 17 00:00:00 2001 From: Won Park Date: Tue, 14 Jul 2026 04:07:13 +0000 Subject: [PATCH] Restrict Guardian reviewer tools (#32945) ## What changed - Limit Guardian reviewer sessions with an environment to `exec_command`, `write_stdin`, and `view_image`. - Exclude general tool sources from Guardian reviewer tool planning. ## Testing - Extend the Guardian request snapshot test to assert the exact model-visible tool list. GitOrigin-RevId: 80ba7011c23c32d163aaad906087fac396cc70fa --- codex-rs/core/src/guardian/tests.rs | 10 ++++++++++ codex-rs/core/src/tools/spec_plan.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/codex-rs/core/src/guardian/tests.rs b/codex-rs/core/src/guardian/tests.rs index f735bdbeb2..4524ab42b8 100644 --- a/codex-rs/core/src/guardian/tests.rs +++ b/codex-rs/core/src/guardian/tests.rs @@ -1743,6 +1743,16 @@ async fn guardian_review_request_layout_matches_model_visible_request_snapshot() )); let request = request_log.single_request(); let request_body = request.body_json(); + let guardian_tool_names = request_body["tools"] + .as_array() + .expect("guardian request tools") + .iter() + .map(|tool| tool["name"].as_str().expect("guardian request tool name")) + .collect::>(); + assert_eq!( + guardian_tool_names, + vec!["exec_command", "write_stdin", "view_image"] + ); let guardian_user_text = request.message_input_texts("user").join("\n"); assert!( guardian_user_text.contains(&format!("${GUARDIAN_SKILL_NAME}")), diff --git a/codex-rs/core/src/tools/spec_plan.rs b/codex-rs/core/src/tools/spec_plan.rs index e64c63b843..ace4c85fd1 100644 --- a/codex-rs/core/src/tools/spec_plan.rs +++ b/codex-rs/core/src/tools/spec_plan.rs @@ -582,6 +582,33 @@ fn code_mode_namespace_descriptions( #[instrument(level = "trace", skip_all)] fn add_tool_sources(context: &CoreToolPlanContext<'_>, planned_tools: &mut PlannedTools) { + // Guardian reviewers receive only `exec_command`, `write_stdin`, and `view_image` + // when an environment is available; all general tool sources stay excluded. + if crate::guardian::is_guardian_reviewer_source(&context.step_context.turn.session_source) { + let turn_context = context.step_context.turn.as_ref(); + let environment_mode = tool_environment_mode(context.step_context); + if environment_mode.has_environment() { + let include_environment_id = matches!(environment_mode, ToolEnvironmentMode::Multiple); + planned_tools.add(ExecCommandHandler::new(ExecCommandHandlerOptions { + allow_login_shell: turn_context.config.permissions.allow_login_shell, + exec_permission_approvals_enabled: false, + include_environment_id, + include_shell_parameter: unified_exec_should_include_shell_parameter( + turn_context, + context.step_context, + ), + })); + planned_tools.add(WriteStdinHandler); + planned_tools.add(ViewImageHandler::new(ViewImageToolOptions { + can_request_original_image_detail: can_request_original_image_detail( + &turn_context.model_info, + ), + include_environment_id, + })); + } + return; + } + add_shell_tools(context, planned_tools); add_mcp_resource_tools(context, planned_tools); add_core_utility_tools(context, planned_tools);