From 67c1c7c054dc494044e091acf17b3f666d5b7658 Mon Sep 17 00:00:00 2001 From: Dylan Hurd Date: Mon, 23 Mar 2026 15:19:01 -0700 Subject: [PATCH] chore(core) Add approvals reviewer to UserTurn (#15426) ## Summary Adds support for approvals_reviewer to `Op::UserTurn` so we can migrate `[CodexMessageProcessor::turn_start]` to use Op::UserTurn ## Testing - [x] Adds quick test for the new field Co-authored-by: Codex --- codex-rs/core/src/codex.rs | 3 +- codex-rs/core/src/codex_tests.rs | 35 +++++++++++++++++++ codex-rs/core/src/guardian/review_session.rs | 1 + codex-rs/core/tests/common/test_codex.rs | 1 + codex-rs/core/tests/suite/apply_patch_cli.rs | 7 ++++ codex-rs/core/tests/suite/approvals.rs | 1 + codex-rs/core/tests/suite/client.rs | 2 ++ codex-rs/core/tests/suite/code_mode.rs | 1 + .../tests/suite/collaboration_instructions.rs | 2 ++ codex-rs/core/tests/suite/compact.rs | 7 ++++ codex-rs/core/tests/suite/exec_policy.rs | 2 ++ codex-rs/core/tests/suite/image_rollout.rs | 2 ++ codex-rs/core/tests/suite/items.rs | 5 +++ codex-rs/core/tests/suite/json_result.rs | 1 + codex-rs/core/tests/suite/live_reload.rs | 1 + codex-rs/core/tests/suite/model_switching.rs | 14 ++++++++ .../core/tests/suite/model_visible_layout.rs | 5 +++ codex-rs/core/tests/suite/models_cache_ttl.rs | 1 + .../core/tests/suite/models_etag_responses.rs | 1 + codex-rs/core/tests/suite/personality.rs | 13 +++++++ codex-rs/core/tests/suite/prompt_caching.rs | 5 +++ codex-rs/core/tests/suite/remote_models.rs | 4 +++ .../core/tests/suite/request_permissions.rs | 1 + .../tests/suite/request_permissions_tool.rs | 1 + .../core/tests/suite/request_user_input.rs | 2 ++ codex-rs/core/tests/suite/rmcp_client.rs | 6 ++++ .../tests/suite/safety_check_downgrade.rs | 4 +++ codex-rs/core/tests/suite/shell_snapshot.rs | 4 +++ codex-rs/core/tests/suite/skill_approval.rs | 1 + codex-rs/core/tests/suite/skills.rs | 1 + codex-rs/core/tests/suite/sqlite_state.rs | 1 + codex-rs/core/tests/suite/tool_harness.rs | 5 +++ codex-rs/core/tests/suite/tool_parallelism.rs | 2 ++ codex-rs/core/tests/suite/truncation.rs | 1 + codex-rs/core/tests/suite/unified_exec.rs | 26 ++++++++++++++ codex-rs/core/tests/suite/user_shell_cmd.rs | 1 + codex-rs/core/tests/suite/view_image.rs | 14 ++++++++ .../core/tests/suite/websocket_fallback.rs | 1 + codex-rs/docs/protocol_v1.md | 2 +- codex-rs/protocol/src/protocol.rs | 5 +++ codex-rs/tui/src/chatwidget.rs | 1 + codex-rs/tui_app_server/src/app.rs | 4 ++- codex-rs/tui_app_server/src/app_command.rs | 4 +++ 43 files changed, 198 insertions(+), 3 deletions(-) diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index 9b8102552..5e428362f 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -4550,6 +4550,7 @@ mod handlers { Op::UserTurn { cwd, approval_policy, + approvals_reviewer, sandbox_policy, model, effort, @@ -4575,7 +4576,7 @@ mod handlers { SessionSettingsUpdate { cwd: Some(cwd), approval_policy: Some(approval_policy), - approvals_reviewer: None, + approvals_reviewer, sandbox_policy: Some(sandbox_policy), windows_sandbox_level: None, collaboration_mode, diff --git a/codex-rs/core/src/codex_tests.rs b/codex-rs/core/src/codex_tests.rs index 24470051f..5d6769e0e 100644 --- a/codex-rs/core/src/codex_tests.rs +++ b/codex-rs/core/src/codex_tests.rs @@ -3013,6 +3013,41 @@ fn op_kind_distinguishes_turn_ops() { ); } +#[tokio::test] +async fn user_turn_updates_approvals_reviewer() { + let (session, turn_context, _rx) = make_session_and_context_with_rx().await; + let config = session.get_config().await; + + handlers::user_input_or_turn( + &session, + "sub-1".to_string(), + Op::UserTurn { + items: vec![UserInput::Text { + text: "hello".to_string(), + text_elements: Vec::new(), + }], + cwd: config.cwd.clone(), + approval_policy: config.permissions.approval_policy.value(), + approvals_reviewer: Some(crate::config::types::ApprovalsReviewer::GuardianSubagent), + sandbox_policy: config.permissions.sandbox_policy.get().clone(), + model: turn_context.model_info.slug.clone(), + effort: config.model_reasoning_effort, + summary: config.model_reasoning_summary, + service_tier: None, + final_output_json_schema: None, + collaboration_mode: None, + personality: config.personality, + }, + ) + .await; + + let state = session.state.lock().await; + assert_eq!( + state.session_configuration.approvals_reviewer, + crate::config::types::ApprovalsReviewer::GuardianSubagent + ); +} + #[tokio::test] async fn spawn_task_turn_span_inherits_dispatch_trace_context() { struct TraceCaptureTask { diff --git a/codex-rs/core/src/guardian/review_session.rs b/codex-rs/core/src/guardian/review_session.rs index 729e17238..50bf2ed84 100644 --- a/codex-rs/core/src/guardian/review_session.rs +++ b/codex-rs/core/src/guardian/review_session.rs @@ -514,6 +514,7 @@ async fn run_review_on_session( items: params.prompt_items.clone(), cwd: params.parent_turn.cwd.clone(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: params.model.clone(), effort: params.reasoning_effort, diff --git a/codex-rs/core/tests/common/test_codex.rs b/codex-rs/core/tests/common/test_codex.rs index 6df93bcd8..c61631e5b 100644 --- a/codex-rs/core/tests/common/test_codex.rs +++ b/codex-rs/core/tests/common/test_codex.rs @@ -723,6 +723,7 @@ impl TestCodex { final_output_json_schema: None, cwd: self.config.cwd.clone(), approval_policy, + approvals_reviewer: None, sandbox_policy, model: session_model, effort: None, diff --git a/codex-rs/core/tests/suite/apply_patch_cli.rs b/codex-rs/core/tests/suite/apply_patch_cli.rs index b113fc465..d0cefcb61 100644 --- a/codex-rs/core/tests/suite/apply_patch_cli.rs +++ b/codex-rs/core/tests/suite/apply_patch_cli.rs @@ -310,6 +310,7 @@ async fn apply_patch_cli_move_without_content_change_has_no_turn_diff( final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model, effort: None, @@ -919,6 +920,7 @@ async fn apply_patch_shell_command_heredoc_with_cd_emits_turn_diff() -> Result<( final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model, effort: None, @@ -1000,6 +1002,7 @@ async fn apply_patch_shell_command_failure_propagates_error_and_skips_diff() -> final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model, effort: None, @@ -1151,6 +1154,7 @@ async fn apply_patch_emits_turn_diff_event_with_unified_diff( final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model, effort: None, @@ -1215,6 +1219,7 @@ async fn apply_patch_turn_diff_for_rename_with_content_change( final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model, effort: None, @@ -1287,6 +1292,7 @@ async fn apply_patch_aggregates_diff_across_multiple_tool_calls() -> Result<()> final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model, effort: None, @@ -1359,6 +1365,7 @@ async fn apply_patch_aggregates_diff_preserves_success_after_failure() -> Result final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model, effort: None, diff --git a/codex-rs/core/tests/suite/approvals.rs b/codex-rs/core/tests/suite/approvals.rs index f77697d01..6ec05bedc 100644 --- a/codex-rs/core/tests/suite/approvals.rs +++ b/codex-rs/core/tests/suite/approvals.rs @@ -558,6 +558,7 @@ async fn submit_turn( final_output_json_schema: None, cwd: test.cwd.path().to_path_buf(), approval_policy, + approvals_reviewer: None, sandbox_policy, model: session_model, effort: None, diff --git a/codex-rs/core/tests/suite/client.rs b/codex-rs/core/tests/suite/client.rs index 3ea30c596..5da71f556 100644 --- a/codex-rs/core/tests/suite/client.rs +++ b/codex-rs/core/tests/suite/client.rs @@ -1310,6 +1310,7 @@ async fn user_turn_collaboration_mode_overrides_model_and_effort() -> anyhow::Re }], cwd: config.cwd.clone(), approval_policy: config.permissions.approval_policy.value(), + approvals_reviewer: None, sandbox_policy: config.permissions.sandbox_policy.get().clone(), model: session_configured.model.clone(), effort: Some(ReasoningEffort::Low), @@ -1427,6 +1428,7 @@ async fn user_turn_explicit_reasoning_summary_overrides_model_catalog_default() }], cwd: config.cwd.clone(), approval_policy: config.permissions.approval_policy.value(), + approvals_reviewer: None, sandbox_policy: config.permissions.sandbox_policy.get().clone(), model: session_configured.model, effort: None, diff --git a/codex-rs/core/tests/suite/code_mode.rs b/codex-rs/core/tests/suite/code_mode.rs index b9e4f05b3..fa8229ecc 100644 --- a/codex-rs/core/tests/suite/code_mode.rs +++ b/codex-rs/core/tests/suite/code_mode.rs @@ -2319,6 +2319,7 @@ text( final_output_json_schema: None, cwd: test.cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: test.session_configured.model.clone(), effort: None, diff --git a/codex-rs/core/tests/suite/collaboration_instructions.rs b/codex-rs/core/tests/suite/collaboration_instructions.rs index 81d0678ca..1517d9968 100644 --- a/codex-rs/core/tests/suite/collaboration_instructions.rs +++ b/codex-rs/core/tests/suite/collaboration_instructions.rs @@ -178,6 +178,7 @@ async fn collaboration_instructions_added_on_user_turn() -> Result<()> { }], cwd: test.config.cwd.clone(), approval_policy: test.config.permissions.approval_policy.value(), + approvals_reviewer: None, sandbox_policy: test.config.permissions.sandbox_policy.get().clone(), model: test.session_configured.model.clone(), effort: None, @@ -293,6 +294,7 @@ async fn user_turn_overrides_collaboration_instructions_after_override() -> Resu }], cwd: test.config.cwd.clone(), approval_policy: test.config.permissions.approval_policy.value(), + approvals_reviewer: None, sandbox_policy: test.config.permissions.sandbox_policy.get().clone(), model: test.session_configured.model.clone(), effort: None, diff --git a/codex-rs/core/tests/suite/compact.rs b/codex-rs/core/tests/suite/compact.rs index 2f4365a95..a54d74011 100644 --- a/codex-rs/core/tests/suite/compact.rs +++ b/codex-rs/core/tests/suite/compact.rs @@ -1658,6 +1658,7 @@ async fn auto_compact_runs_after_resume_when_token_usage_is_over_limit() { final_output_json_schema: None, cwd: resumed.cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: resumed.session_configured.model.clone(), effort: None, @@ -1748,6 +1749,7 @@ async fn pre_sampling_compact_runs_on_switch_to_smaller_context_model() { final_output_json_schema: None, cwd: test.cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: previous_model.to_string(), effort: None, @@ -1772,6 +1774,7 @@ async fn pre_sampling_compact_runs_on_switch_to_smaller_context_model() { final_output_json_schema: None, cwd: test.cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: next_model.to_string(), effort: None, @@ -1882,6 +1885,7 @@ async fn pre_sampling_compact_runs_after_resume_and_switch_to_smaller_model() { final_output_json_schema: None, cwd: initial.cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: previous_model.to_string(), effort: None, @@ -1930,6 +1934,7 @@ async fn pre_sampling_compact_runs_after_resume_and_switch_to_smaller_model() { final_output_json_schema: None, cwd: resumed.cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: next_model.to_string(), effort: None, @@ -3132,6 +3137,7 @@ async fn snapshot_request_shape_pre_turn_compaction_strips_incoming_model_switch final_output_json_schema: None, cwd: test.cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: previous_model.to_string(), effort: None, @@ -3156,6 +3162,7 @@ async fn snapshot_request_shape_pre_turn_compaction_strips_incoming_model_switch final_output_json_schema: None, cwd: test.cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: next_model.to_string(), effort: None, diff --git a/codex-rs/core/tests/suite/exec_policy.rs b/codex-rs/core/tests/suite/exec_policy.rs index 18be46802..fb055c970 100644 --- a/codex-rs/core/tests/suite/exec_policy.rs +++ b/codex-rs/core/tests/suite/exec_policy.rs @@ -51,6 +51,7 @@ async fn submit_user_turn( final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy, + approvals_reviewer: None, sandbox_policy, model: session_model, effort: None, @@ -131,6 +132,7 @@ async fn execpolicy_blocks_shell_invocation() -> Result<()> { final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, diff --git a/codex-rs/core/tests/suite/image_rollout.rs b/codex-rs/core/tests/suite/image_rollout.rs index 07b51c762..1526fe11a 100644 --- a/codex-rs/core/tests/suite/image_rollout.rs +++ b/codex-rs/core/tests/suite/image_rollout.rs @@ -122,6 +122,7 @@ async fn copy_paste_local_image_persists_rollout_request_shape() -> anyhow::Resu final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -205,6 +206,7 @@ async fn drag_drop_image_persists_rollout_request_shape() -> anyhow::Result<()> final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, diff --git a/codex-rs/core/tests/suite/items.rs b/codex-rs/core/tests/suite/items.rs index d86054555..f949cd4b9 100644 --- a/codex-rs/core/tests/suite/items.rs +++ b/codex-rs/core/tests/suite/items.rs @@ -522,6 +522,7 @@ async fn plan_mode_emits_plan_item_from_proposed_plan_block() -> anyhow::Result< final_output_json_schema: None, cwd: std::env::current_dir()?, approval_policy: codex_protocol::protocol::AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: codex_protocol::protocol::SandboxPolicy::DangerFullAccess, model: session_configured.model.clone(), effort: None, @@ -598,6 +599,7 @@ async fn plan_mode_strips_plan_from_agent_messages() -> anyhow::Result<()> { final_output_json_schema: None, cwd: std::env::current_dir()?, approval_policy: codex_protocol::protocol::AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: codex_protocol::protocol::SandboxPolicy::DangerFullAccess, model: session_configured.model.clone(), effort: None, @@ -706,6 +708,7 @@ async fn plan_mode_streaming_citations_are_stripped_across_added_deltas_and_done final_output_json_schema: None, cwd: std::env::current_dir()?, approval_policy: codex_protocol::protocol::AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: codex_protocol::protocol::SandboxPolicy::DangerFullAccess, model: session_configured.model.clone(), effort: None, @@ -892,6 +895,7 @@ async fn plan_mode_streaming_proposed_plan_tag_split_across_added_and_delta_is_p final_output_json_schema: None, cwd: std::env::current_dir()?, approval_policy: codex_protocol::protocol::AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: codex_protocol::protocol::SandboxPolicy::DangerFullAccess, model: session_configured.model.clone(), effort: None, @@ -1005,6 +1009,7 @@ async fn plan_mode_handles_missing_plan_close_tag() -> anyhow::Result<()> { final_output_json_schema: None, cwd: std::env::current_dir()?, approval_policy: codex_protocol::protocol::AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: codex_protocol::protocol::SandboxPolicy::DangerFullAccess, model: session_configured.model.clone(), effort: None, diff --git a/codex-rs/core/tests/suite/json_result.rs b/codex-rs/core/tests/suite/json_result.rs index f7dfd02da..3b6f3e3f9 100644 --- a/codex-rs/core/tests/suite/json_result.rs +++ b/codex-rs/core/tests/suite/json_result.rs @@ -80,6 +80,7 @@ async fn codex_returns_json_result(model: String) -> anyhow::Result<()> { final_output_json_schema: Some(serde_json::from_str(SCHEMA)?), cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model, effort: None, diff --git a/codex-rs/core/tests/suite/live_reload.rs b/codex-rs/core/tests/suite/live_reload.rs index 2192bdffd..663cf4748 100644 --- a/codex-rs/core/tests/suite/live_reload.rs +++ b/codex-rs/core/tests/suite/live_reload.rs @@ -61,6 +61,7 @@ async fn submit_skill_turn(test: &TestCodex, skill_path: PathBuf, prompt: &str) final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, diff --git a/codex-rs/core/tests/suite/model_switching.rs b/codex-rs/core/tests/suite/model_switching.rs index 9902f0ee6..a7c5ceb0d 100644 --- a/codex-rs/core/tests/suite/model_switching.rs +++ b/codex-rs/core/tests/suite/model_switching.rs @@ -126,6 +126,7 @@ async fn model_change_appends_model_instructions_developer_message() -> Result<( final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, @@ -162,6 +163,7 @@ async fn model_change_appends_model_instructions_developer_message() -> Result<( final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: next_model.to_string(), effort: test.config.model_reasoning_effort, @@ -221,6 +223,7 @@ async fn model_and_personality_change_only_appends_model_instructions() -> Resul final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, @@ -257,6 +260,7 @@ async fn model_and_personality_change_only_appends_model_instructions() -> Resul final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: next_model.to_string(), effort: test.config.model_reasoning_effort, @@ -398,6 +402,7 @@ async fn model_change_from_image_to_text_strips_prior_image_content() -> Result< final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: image_model_slug.to_string(), effort: test.config.model_reasoning_effort, @@ -418,6 +423,7 @@ async fn model_change_from_image_to_text_strips_prior_image_content() -> Result< final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: text_model_slug.to_string(), effort: test.config.model_reasoning_effort, @@ -525,6 +531,7 @@ async fn generated_image_is_replayed_for_image_capable_models() -> Result<()> { final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: image_model_slug.to_string(), effort: test.config.model_reasoning_effort, @@ -545,6 +552,7 @@ async fn generated_image_is_replayed_for_image_capable_models() -> Result<()> { final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: image_model_slug.to_string(), effort: test.config.model_reasoning_effort, @@ -655,6 +663,7 @@ async fn model_change_from_generated_image_to_text_preserves_prior_generated_ima final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: image_model_slug.to_string(), effort: test.config.model_reasoning_effort, @@ -675,6 +684,7 @@ async fn model_change_from_generated_image_to_text_preserves_prior_generated_ima final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: text_model_slug.to_string(), effort: test.config.model_reasoning_effort, @@ -787,6 +797,7 @@ async fn thread_rollback_after_generated_image_drops_entire_image_turn_history() final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: image_model_slug.to_string(), effort: test.config.model_reasoning_effort, @@ -815,6 +826,7 @@ async fn thread_rollback_after_generated_image_drops_entire_image_turn_history() final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: image_model_slug.to_string(), effort: test.config.model_reasoning_effort, @@ -969,6 +981,7 @@ async fn model_switch_to_smaller_model_updates_token_context_window() -> Result< final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: large_model_slug.to_string(), effort: test.config.model_reasoning_effort, @@ -1027,6 +1040,7 @@ async fn model_switch_to_smaller_model_updates_token_context_window() -> Result< final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: smaller_model_slug.to_string(), effort: test.config.model_reasoning_effort, diff --git a/codex-rs/core/tests/suite/model_visible_layout.rs b/codex-rs/core/tests/suite/model_visible_layout.rs index a10fa7c26..49f635432 100644 --- a/codex-rs/core/tests/suite/model_visible_layout.rs +++ b/codex-rs/core/tests/suite/model_visible_layout.rs @@ -121,6 +121,7 @@ async fn snapshot_model_visible_layout_turn_overrides() -> Result<()> { final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, @@ -144,6 +145,7 @@ async fn snapshot_model_visible_layout_turn_overrides() -> Result<()> { final_output_json_schema: None, cwd: preturn_context_diff_cwd, approval_policy: AskForApproval::OnRequest, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, @@ -222,6 +224,7 @@ async fn snapshot_model_visible_layout_cwd_change_does_not_refresh_agents() -> R final_output_json_schema: None, cwd: cwd_one.clone(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, @@ -245,6 +248,7 @@ async fn snapshot_model_visible_layout_cwd_change_does_not_refresh_agents() -> R final_output_json_schema: None, cwd: cwd_two, approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, @@ -354,6 +358,7 @@ async fn snapshot_model_visible_layout_resume_with_personality_change() -> Resul final_output_json_schema: None, cwd: resume_override_cwd, approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: resumed.session_configured.model.clone(), effort: resumed.config.model_reasoning_effort, diff --git a/codex-rs/core/tests/suite/models_cache_ttl.rs b/codex-rs/core/tests/suite/models_cache_ttl.rs index 7cb757334..3d5904bd7 100644 --- a/codex-rs/core/tests/suite/models_cache_ttl.rs +++ b/codex-rs/core/tests/suite/models_cache_ttl.rs @@ -96,6 +96,7 @@ async fn renews_cache_ttl_on_matching_models_etag() -> Result<()> { final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: codex_protocol::protocol::AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: test.session_configured.model.clone(), effort: None, diff --git a/codex-rs/core/tests/suite/models_etag_responses.rs b/codex-rs/core/tests/suite/models_etag_responses.rs index a479ebb80..272c820ca 100644 --- a/codex-rs/core/tests/suite/models_etag_responses.rs +++ b/codex-rs/core/tests/suite/models_etag_responses.rs @@ -102,6 +102,7 @@ async fn refresh_models_on_models_etag_mismatch_and_avoid_duplicate_models_fetch final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, diff --git a/codex-rs/core/tests/suite/personality.rs b/codex-rs/core/tests/suite/personality.rs index 9a495e7af..ed4da0716 100644 --- a/codex-rs/core/tests/suite/personality.rs +++ b/codex-rs/core/tests/suite/personality.rs @@ -103,6 +103,7 @@ async fn user_turn_personality_none_does_not_add_update_message() -> anyhow::Res final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: test.config.permissions.approval_policy.value(), + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, @@ -153,6 +154,7 @@ async fn config_personality_some_sets_instructions_template() -> anyhow::Result< final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: test.config.permissions.approval_policy.value(), + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, @@ -210,6 +212,7 @@ async fn config_personality_none_sends_no_personality() -> anyhow::Result<()> { final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: test.config.permissions.approval_policy.value(), + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, @@ -273,6 +276,7 @@ async fn default_personality_is_pragmatic_without_config_toml() -> anyhow::Resul final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: test.config.permissions.approval_policy.value(), + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, @@ -324,6 +328,7 @@ async fn user_turn_personality_some_adds_update_message() -> anyhow::Result<()> final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: test.config.permissions.approval_policy.value(), + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, @@ -361,6 +366,7 @@ async fn user_turn_personality_some_adds_update_message() -> anyhow::Result<()> final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: test.config.permissions.approval_policy.value(), + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, @@ -427,6 +433,7 @@ async fn user_turn_personality_same_value_does_not_add_update_message() -> anyho final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: test.config.permissions.approval_policy.value(), + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, @@ -464,6 +471,7 @@ async fn user_turn_personality_same_value_does_not_add_update_message() -> anyho final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: test.config.permissions.approval_policy.value(), + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, @@ -543,6 +551,7 @@ async fn user_turn_personality_skips_if_feature_disabled() -> anyhow::Result<()> final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: test.config.permissions.approval_policy.value(), + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, @@ -580,6 +589,7 @@ async fn user_turn_personality_skips_if_feature_disabled() -> anyhow::Result<()> final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: test.config.permissions.approval_policy.value(), + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, @@ -696,6 +706,7 @@ async fn remote_model_friendly_personality_instructions_with_feature() -> anyhow final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: remote_slug.to_string(), effort: test.config.model_reasoning_effort, @@ -814,6 +825,7 @@ async fn user_turn_personality_remote_model_template_includes_update_message() - final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: remote_slug.to_string(), effort: test.config.model_reasoning_effort, @@ -851,6 +863,7 @@ async fn user_turn_personality_remote_model_template_includes_update_message() - final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: remote_slug.to_string(), effort: test.config.model_reasoning_effort, diff --git a/codex-rs/core/tests/suite/prompt_caching.rs b/codex-rs/core/tests/suite/prompt_caching.rs index 14caaf8f0..9be83e6bb 100644 --- a/codex-rs/core/tests/suite/prompt_caching.rs +++ b/codex-rs/core/tests/suite/prompt_caching.rs @@ -703,6 +703,7 @@ async fn per_turn_overrides_keep_cached_prefix_and_key_constant() -> anyhow::Res }], cwd: new_cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: new_policy.clone(), model: "o3".to_string(), effort: Some(ReasoningEffort::High), @@ -815,6 +816,7 @@ async fn send_user_turn_with_no_changes_does_not_send_environment_context() -> a }], cwd: default_cwd.clone(), approval_policy: default_approval_policy, + approvals_reviewer: None, sandbox_policy: default_sandbox_policy.clone(), model: default_model.clone(), effort: default_effort, @@ -835,6 +837,7 @@ async fn send_user_turn_with_no_changes_does_not_send_environment_context() -> a }], cwd: default_cwd.clone(), approval_policy: default_approval_policy, + approvals_reviewer: None, sandbox_policy: default_sandbox_policy.clone(), model: default_model.clone(), effort: default_effort, @@ -939,6 +942,7 @@ async fn send_user_turn_with_changes_sends_environment_context() -> anyhow::Resu }], cwd: default_cwd.clone(), approval_policy: default_approval_policy, + approvals_reviewer: None, sandbox_policy: default_sandbox_policy.clone(), model: default_model, effort: default_effort, @@ -959,6 +963,7 @@ async fn send_user_turn_with_changes_sends_environment_context() -> anyhow::Resu }], cwd: default_cwd.clone(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: "o3".to_string(), effort: Some(ReasoningEffort::High), diff --git a/codex-rs/core/tests/suite/remote_models.rs b/codex-rs/core/tests/suite/remote_models.rs index 860d83fe9..83d911533 100644 --- a/codex-rs/core/tests/suite/remote_models.rs +++ b/codex-rs/core/tests/suite/remote_models.rs @@ -173,6 +173,7 @@ async fn remote_models_long_model_slug_is_sent_with_high_reasoning() -> Result<( final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: config.permissions.approval_policy.value(), + approvals_reviewer: None, sandbox_policy: config.permissions.sandbox_policy.get().clone(), model: requested_model.to_string(), effort: None, @@ -231,6 +232,7 @@ async fn namespaced_model_slug_uses_catalog_metadata_without_fallback_warning() final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: config.permissions.approval_policy.value(), + approvals_reviewer: None, sandbox_policy: config.permissions.sandbox_policy.get().clone(), model: requested_model.to_string(), effort: None, @@ -394,6 +396,7 @@ async fn remote_models_remote_model_uses_unified_exec() -> Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: REMOTE_MODEL_SLUG.to_string(), effort: None, @@ -612,6 +615,7 @@ async fn remote_models_apply_remote_base_instructions() -> Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: model.to_string(), effort: None, diff --git a/codex-rs/core/tests/suite/request_permissions.rs b/codex-rs/core/tests/suite/request_permissions.rs index b1aaac65b..7c16599bf 100644 --- a/codex-rs/core/tests/suite/request_permissions.rs +++ b/codex-rs/core/tests/suite/request_permissions.rs @@ -193,6 +193,7 @@ async fn submit_turn( final_output_json_schema: None, cwd: test.cwd.path().to_path_buf(), approval_policy, + approvals_reviewer: None, sandbox_policy, model: session_model, effort: None, diff --git a/codex-rs/core/tests/suite/request_permissions_tool.rs b/codex-rs/core/tests/suite/request_permissions_tool.rs index a01d6e0ab..14506f4a4 100644 --- a/codex-rs/core/tests/suite/request_permissions_tool.rs +++ b/codex-rs/core/tests/suite/request_permissions_tool.rs @@ -145,6 +145,7 @@ async fn submit_turn( final_output_json_schema: None, cwd: test.cwd.path().to_path_buf(), approval_policy, + approvals_reviewer: None, sandbox_policy, model: session_model, effort: None, diff --git a/codex-rs/core/tests/suite/request_user_input.rs b/codex-rs/core/tests/suite/request_user_input.rs index 1bf759d27..8e30b37c2 100644 --- a/codex-rs/core/tests/suite/request_user_input.rs +++ b/codex-rs/core/tests/suite/request_user_input.rs @@ -138,6 +138,7 @@ async fn request_user_input_round_trip_for_mode(mode: ModeKind) -> anyhow::Resul final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -255,6 +256,7 @@ where final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, diff --git a/codex-rs/core/tests/suite/rmcp_client.rs b/codex-rs/core/tests/suite/rmcp_client.rs index 772674f79..6cbf9521b 100644 --- a/codex-rs/core/tests/suite/rmcp_client.rs +++ b/codex-rs/core/tests/suite/rmcp_client.rs @@ -129,6 +129,7 @@ async fn stdio_server_round_trip() -> anyhow::Result<()> { final_output_json_schema: None, cwd: fixture.cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: session_model, effort: None, @@ -298,6 +299,7 @@ async fn stdio_image_responses_round_trip() -> anyhow::Result<()> { final_output_json_schema: None, cwd: fixture.cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: session_model, effort: None, @@ -501,6 +503,7 @@ async fn stdio_image_responses_are_sanitized_for_text_only_model() -> anyhow::Re final_output_json_schema: None, cwd: fixture.cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: text_only_model_slug.to_string(), effort: None, @@ -615,6 +618,7 @@ async fn stdio_server_propagates_whitelisted_env_vars() -> anyhow::Result<()> { final_output_json_schema: None, cwd: fixture.cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: session_model, effort: None, @@ -776,6 +780,7 @@ async fn streamable_http_tool_call_round_trip() -> anyhow::Result<()> { final_output_json_schema: None, cwd: fixture.cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: session_model, effort: None, @@ -1022,6 +1027,7 @@ async fn streamable_http_with_oauth_round_trip_impl() -> anyhow::Result<()> { final_output_json_schema: None, cwd: fixture.cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: session_model, effort: None, diff --git a/codex-rs/core/tests/suite/safety_check_downgrade.rs b/codex-rs/core/tests/suite/safety_check_downgrade.rs index eabbdc9d0..51a88ef16 100644 --- a/codex-rs/core/tests/suite/safety_check_downgrade.rs +++ b/codex-rs/core/tests/suite/safety_check_downgrade.rs @@ -45,6 +45,7 @@ async fn openai_model_header_mismatch_emits_warning_event_and_warning_item() -> final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: REQUESTED_MODEL.to_string(), effort: test.config.model_reasoning_effort, @@ -143,6 +144,7 @@ async fn response_model_field_mismatch_emits_warning_when_header_matches_request final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: REQUESTED_MODEL.to_string(), effort: test.config.model_reasoning_effort, @@ -228,6 +230,7 @@ async fn openai_model_header_mismatch_only_emits_one_warning_per_turn() -> Resul final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: REQUESTED_MODEL.to_string(), effort: test.config.model_reasoning_effort, @@ -277,6 +280,7 @@ async fn openai_model_header_casing_only_mismatch_does_not_warn() -> Result<()> final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: REQUESTED_MODEL.to_string(), effort: test.config.model_reasoning_effort, diff --git a/codex-rs/core/tests/suite/shell_snapshot.rs b/codex-rs/core/tests/suite/shell_snapshot.rs index 68228a412..55bb0ac28 100644 --- a/codex-rs/core/tests/suite/shell_snapshot.rs +++ b/codex-rs/core/tests/suite/shell_snapshot.rs @@ -164,6 +164,7 @@ async fn run_snapshot_command_with_options( final_output_json_schema: None, cwd, approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -254,6 +255,7 @@ async fn run_shell_command_snapshot_with_options( final_output_json_schema: None, cwd, approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -324,6 +326,7 @@ async fn run_tool_turn_on_harness( final_output_json_schema: None, cwd, approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -555,6 +558,7 @@ async fn shell_command_snapshot_still_intercepts_apply_patch() -> Result<()> { final_output_json_schema: None, cwd: cwd.clone(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model, effort: None, diff --git a/codex-rs/core/tests/suite/skill_approval.rs b/codex-rs/core/tests/suite/skill_approval.rs index b5fda12ae..5a50e09ce 100644 --- a/codex-rs/core/tests/suite/skill_approval.rs +++ b/codex-rs/core/tests/suite/skill_approval.rs @@ -65,6 +65,7 @@ async fn submit_turn_with_policies( final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy, + approvals_reviewer: None, sandbox_policy, model: test.session_configured.model.clone(), effort: None, diff --git a/codex-rs/core/tests/suite/skills.rs b/codex-rs/core/tests/suite/skills.rs index 801e0dd6b..388618b56 100644 --- a/codex-rs/core/tests/suite/skills.rs +++ b/codex-rs/core/tests/suite/skills.rs @@ -74,6 +74,7 @@ async fn user_turn_includes_skill_instructions() -> Result<()> { final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, diff --git a/codex-rs/core/tests/suite/sqlite_state.rs b/codex-rs/core/tests/suite/sqlite_state.rs index 248ada02c..2df92dbf9 100644 --- a/codex-rs/core/tests/suite/sqlite_state.rs +++ b/codex-rs/core/tests/suite/sqlite_state.rs @@ -395,6 +395,7 @@ async fn mcp_call_marks_thread_memory_mode_polluted_when_configured() -> Result< final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: test.session_configured.model.clone(), effort: None, diff --git a/codex-rs/core/tests/suite/tool_harness.rs b/codex-rs/core/tests/suite/tool_harness.rs index bb1da9e8b..9594195a5 100644 --- a/codex-rs/core/tests/suite/tool_harness.rs +++ b/codex-rs/core/tests/suite/tool_harness.rs @@ -85,6 +85,7 @@ async fn shell_tool_executes_command_and_streams_output() -> anyhow::Result<()> final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -155,6 +156,7 @@ async fn update_plan_tool_emits_plan_update_event() -> anyhow::Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -235,6 +237,7 @@ async fn update_plan_tool_rejects_malformed_payload() -> anyhow::Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -330,6 +333,7 @@ async fn apply_patch_tool_executes_and_emits_patch_events() -> anyhow::Result<() final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -433,6 +437,7 @@ async fn apply_patch_reports_parse_diagnostics() -> anyhow::Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, diff --git a/codex-rs/core/tests/suite/tool_parallelism.rs b/codex-rs/core/tests/suite/tool_parallelism.rs index a177a6ee3..faff0b2e0 100644 --- a/codex-rs/core/tests/suite/tool_parallelism.rs +++ b/codex-rs/core/tests/suite/tool_parallelism.rs @@ -42,6 +42,7 @@ async fn run_turn(test: &TestCodex, prompt: &str) -> anyhow::Result<()> { final_output_json_schema: None, cwd: test.cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -358,6 +359,7 @@ async fn shell_tools_start_before_response_completed_when_stream_delayed() -> an final_output_json_schema: None, cwd: test.cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, diff --git a/codex-rs/core/tests/suite/truncation.rs b/codex-rs/core/tests/suite/truncation.rs index e7b25799e..f10567de2 100644 --- a/codex-rs/core/tests/suite/truncation.rs +++ b/codex-rs/core/tests/suite/truncation.rs @@ -486,6 +486,7 @@ async fn mcp_image_output_preserves_image_and_no_text_summary() -> Result<()> { final_output_json_schema: None, cwd: fixture.cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: session_model, effort: None, diff --git a/codex-rs/core/tests/suite/unified_exec.rs b/codex-rs/core/tests/suite/unified_exec.rs index 7252d9a6b..c07fd20e5 100644 --- a/codex-rs/core/tests/suite/unified_exec.rs +++ b/codex-rs/core/tests/suite/unified_exec.rs @@ -192,6 +192,7 @@ async fn unified_exec_intercepts_apply_patch_exec_command() -> Result<()> { final_output_json_schema: None, cwd, approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -325,6 +326,7 @@ async fn unified_exec_emits_exec_command_begin_event() -> Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -407,6 +409,7 @@ async fn unified_exec_resolves_relative_workdir() -> Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -492,6 +495,7 @@ async fn unified_exec_respects_workdir_override() -> Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -589,6 +593,7 @@ async fn unified_exec_emits_exec_command_end_event() -> Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -668,6 +673,7 @@ async fn unified_exec_emits_output_delta_for_exec_command() -> Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -748,6 +754,7 @@ async fn unified_exec_full_lifecycle_with_background_end_event() -> Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -882,6 +889,7 @@ async fn unified_exec_emits_terminal_interaction_for_write_stdin() -> Result<()> final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -1023,6 +1031,7 @@ async fn unified_exec_terminal_interaction_captures_delayed_output() -> Result<( final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -1187,6 +1196,7 @@ async fn unified_exec_emits_one_begin_and_one_end_event() -> Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -1289,6 +1299,7 @@ async fn exec_command_reports_chunk_and_exit_metadata() -> Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -1411,6 +1422,7 @@ async fn unified_exec_defaults_to_pipe() -> Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -1505,6 +1517,7 @@ async fn unified_exec_can_enable_tty() -> Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -1590,6 +1603,7 @@ async fn unified_exec_respects_early_exit_notifications() -> Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -1725,6 +1739,7 @@ async fn write_stdin_returns_exit_metadata_and_clears_session() -> Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -1897,6 +1912,7 @@ async fn unified_exec_emits_end_event_when_session_dies_via_stdin() -> Result<() final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -1978,6 +1994,7 @@ async fn unified_exec_keeps_long_running_session_after_turn_end() -> Result<()> final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -2070,6 +2087,7 @@ async fn unified_exec_interrupt_preserves_long_running_session() -> Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -2178,6 +2196,7 @@ async fn unified_exec_reuses_session_via_stdin() -> Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -2317,6 +2336,7 @@ PY final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -2435,6 +2455,7 @@ async fn unified_exec_timeout_and_followup_poll() -> Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -2535,6 +2556,7 @@ PY final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -2634,6 +2656,7 @@ async fn unified_exec_runs_under_sandbox() -> Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, // Important! sandbox_policy: SandboxPolicy::new_read_only_policy(), model: session_model, @@ -2743,6 +2766,7 @@ async fn unified_exec_python_prompt_under_seatbelt() -> Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), model: session_model, effort: None, @@ -2842,6 +2866,7 @@ async fn unified_exec_runs_on_all_platforms() -> Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -2981,6 +3006,7 @@ async fn unified_exec_prunes_exited_sessions_first() -> Result<()> { final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, diff --git a/codex-rs/core/tests/suite/user_shell_cmd.rs b/codex-rs/core/tests/suite/user_shell_cmd.rs index eb593c6fe..801ff7627 100644 --- a/codex-rs/core/tests/suite/user_shell_cmd.rs +++ b/codex-rs/core/tests/suite/user_shell_cmd.rs @@ -176,6 +176,7 @@ async fn user_shell_command_does_not_replace_active_turn() -> anyhow::Result<()> final_output_json_schema: None, cwd: fixture.cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: fixture.session_configured.model.clone(), effort: None, diff --git a/codex-rs/core/tests/suite/view_image.rs b/codex-rs/core/tests/suite/view_image.rs index efc2e5332..732d6b29e 100644 --- a/codex-rs/core/tests/suite/view_image.rs +++ b/codex-rs/core/tests/suite/view_image.rs @@ -170,6 +170,7 @@ async fn user_turn_with_local_image_attaches_image() -> anyhow::Result<()> { final_output_json_schema: None, cwd: config.cwd.clone(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -278,6 +279,7 @@ async fn view_image_tool_attaches_local_image() -> anyhow::Result<()> { final_output_json_schema: None, cwd: cwd.clone(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -418,6 +420,7 @@ async fn view_image_tool_can_preserve_original_resolution_when_requested_on_gpt5 final_output_json_schema: None, cwd: config.cwd.clone(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -516,6 +519,7 @@ async fn view_image_tool_errors_clearly_for_unsupported_detail_values() -> anyho final_output_json_schema: None, cwd: config.cwd.clone(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -607,6 +611,7 @@ async fn view_image_tool_treats_null_detail_as_omitted() -> anyhow::Result<()> { final_output_json_schema: None, cwd: config.cwd.clone(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -706,6 +711,7 @@ async fn view_image_tool_resizes_when_model_lacks_original_detail_support() -> a final_output_json_schema: None, cwd: config.cwd.clone(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -816,6 +822,7 @@ async fn view_image_tool_does_not_force_original_resolution_with_capability_feat final_output_json_schema: None, cwd: config.cwd.clone(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -917,6 +924,7 @@ await codex.emitImage(out); final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -1036,6 +1044,7 @@ console.log(out.type); final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -1128,6 +1137,7 @@ async fn view_image_tool_errors_when_path_is_directory() -> anyhow::Result<()> { final_output_json_schema: None, cwd: config.cwd.clone(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -1203,6 +1213,7 @@ async fn view_image_tool_errors_for_non_image_files() -> anyhow::Result<()> { final_output_json_schema: None, cwd: config.cwd.clone(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -1283,6 +1294,7 @@ async fn view_image_tool_errors_when_file_missing() -> anyhow::Result<()> { final_output_json_schema: None, cwd: config.cwd.clone(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, @@ -1405,6 +1417,7 @@ async fn view_image_tool_returns_unsupported_message_for_text_only_model() -> an final_output_json_schema: None, cwd: config.cwd.clone(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: model_slug.to_string(), effort: None, @@ -1479,6 +1492,7 @@ async fn replaces_invalid_local_image_after_bad_request() -> anyhow::Result<()> final_output_json_schema: None, cwd: config.cwd.clone(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, diff --git a/codex-rs/core/tests/suite/websocket_fallback.rs b/codex-rs/core/tests/suite/websocket_fallback.rs index 0090093c7..e7f33df14 100644 --- a/codex-rs/core/tests/suite/websocket_fallback.rs +++ b/codex-rs/core/tests/suite/websocket_fallback.rs @@ -156,6 +156,7 @@ async fn websocket_fallback_hides_first_websocket_retry_stream_error() -> Result final_output_json_schema: None, cwd: cwd.path().to_path_buf(), approval_policy: AskForApproval::Never, + approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_configured.model.clone(), effort: None, diff --git a/codex-rs/docs/protocol_v1.md b/codex-rs/docs/protocol_v1.md index 4d4e5c147..9f238b40e 100644 --- a/codex-rs/docs/protocol_v1.md +++ b/codex-rs/docs/protocol_v1.md @@ -65,7 +65,7 @@ Since only 1 `Task` can be run at a time, for parallel tasks it is recommended t For complete documentation of the `Op` and `EventMsg` variants, refer to [protocol.rs](../protocol/src/protocol.rs). Some example payload types: - `Op` - - `Op::UserTurn` – Any input from the user to kick off a `Turn` + - `Op::UserTurn` – Any input from the user to kick off a `Turn`, including full per-turn context such as cwd, model, sandbox, approval policy, and optional `approvals_reviewer` - `Op::UserInput` – Legacy form of user input - `Op::Interrupt` – Interrupts a running turn - `Op::ExecApproval` – Approve or deny code execution diff --git a/codex-rs/protocol/src/protocol.rs b/codex-rs/protocol/src/protocol.rs index e6b1ae79d..b5c96f4d0 100644 --- a/codex-rs/protocol/src/protocol.rs +++ b/codex-rs/protocol/src/protocol.rs @@ -255,6 +255,11 @@ pub enum Op { /// Policy to use for command approval. approval_policy: AskForApproval, + /// Reviewer to use for approval requests raised during this turn. + /// + /// When omitted, the session keeps the current setting + approvals_reviewer: Option, + /// Policy to use for tool calls such as `local_shell`. sandbox_policy: SandboxPolicy, diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index a55324017..ade9235b4 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -5260,6 +5260,7 @@ impl ChatWidget { items, cwd: self.config.cwd.clone(), approval_policy: self.config.permissions.approval_policy.value(), + approvals_reviewer: None, sandbox_policy: self.config.permissions.sandbox_policy.get().clone(), model: effective_mode.model().to_string(), effort: effective_mode.reasoning_effort(), diff --git a/codex-rs/tui_app_server/src/app.rs b/codex-rs/tui_app_server/src/app.rs index 6de3a2a34..bb36d8394 100644 --- a/codex-rs/tui_app_server/src/app.rs +++ b/codex-rs/tui_app_server/src/app.rs @@ -2010,6 +2010,7 @@ impl App { items, cwd, approval_policy, + approvals_reviewer, sandbox_policy, model, effort, @@ -2042,7 +2043,8 @@ impl App { items.to_vec(), cwd.clone(), approval_policy, - self.chat_widget.config_ref().approvals_reviewer, + approvals_reviewer + .unwrap_or(self.chat_widget.config_ref().approvals_reviewer), sandbox_policy.clone(), model.to_string(), effort, diff --git a/codex-rs/tui_app_server/src/app_command.rs b/codex-rs/tui_app_server/src/app_command.rs index ed89ad86f..e01a25027 100644 --- a/codex-rs/tui_app_server/src/app_command.rs +++ b/codex-rs/tui_app_server/src/app_command.rs @@ -42,6 +42,7 @@ pub(crate) enum AppCommandView<'a> { items: &'a [UserInput], cwd: &'a PathBuf, approval_policy: AskForApproval, + approvals_reviewer: &'a Option, sandbox_policy: &'a SandboxPolicy, model: &'a str, effort: Option, @@ -159,6 +160,7 @@ impl AppCommand { items, cwd, approval_policy, + approvals_reviewer: None, sandbox_policy, model, effort, @@ -303,6 +305,7 @@ impl AppCommand { items, cwd, approval_policy, + approvals_reviewer, sandbox_policy, model, effort, @@ -315,6 +318,7 @@ impl AppCommand { items, cwd, approval_policy: *approval_policy, + approvals_reviewer, sandbox_policy, model, effort: *effort,