From e0554ee748b26c11ccd33481b714b020d4874f9b Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Mon, 20 Apr 2026 22:54:43 -0700 Subject: [PATCH] tui: carry permission profiles on user turns --- codex-rs/core/src/guardian/review_session.rs | 1 + codex-rs/core/src/session/handlers.rs | 3 +- codex-rs/core/src/session/tests.rs | 1 + codex-rs/core/tests/common/test_codex.rs | 1 + codex-rs/core/tests/suite/apply_patch_cli.rs | 8 +++ 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/pending_input.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 | 7 ++ .../core/tests/suite/request_permissions.rs | 1 + .../tests/suite/request_permissions_tool.rs | 1 + .../core/tests/suite/request_user_input.rs | 2 + .../suite/responses_api_proxy_headers.rs | 1 + codex-rs/core/tests/suite/rmcp_client.rs | 9 +++ .../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 | 7 ++ 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/protocol/src/protocol.rs | 7 ++ codex-rs/tui/src/app/thread_routing.rs | 2 + codex-rs/tui/src/app_command.rs | 6 ++ codex-rs/tui/src/app_server_session.rs | 20 ++++-- codex-rs/tui/src/chatwidget.rs | 9 +++ .../chatwidget/tests/composer_submission.rs | 69 +++++++++++++++++++ 46 files changed, 246 insertions(+), 8 deletions(-) diff --git a/codex-rs/core/src/guardian/review_session.rs b/codex-rs/core/src/guardian/review_session.rs index 3a8e475744..e5f7a7ae0d 100644 --- a/codex-rs/core/src/guardian/review_session.rs +++ b/codex-rs/core/src/guardian/review_session.rs @@ -593,6 +593,7 @@ async fn run_review_on_session( approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: params.model.clone(), effort: params.reasoning_effort, summary: Some(params.reasoning_summary), diff --git a/codex-rs/core/src/session/handlers.rs b/codex-rs/core/src/session/handlers.rs index 02c9d10938..e2f3b71516 100644 --- a/codex-rs/core/src/session/handlers.rs +++ b/codex-rs/core/src/session/handlers.rs @@ -131,6 +131,7 @@ pub(super) async fn user_input_or_turn_inner( approval_policy, approvals_reviewer, sandbox_policy, + permission_profile, model, effort, summary, @@ -157,7 +158,7 @@ pub(super) async fn user_input_or_turn_inner( approval_policy: Some(approval_policy), approvals_reviewer, sandbox_policy: Some(sandbox_policy), - permission_profile: None, + permission_profile, windows_sandbox_level: None, collaboration_mode, reasoning_summary: summary, diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index ef2d195720..1ad9f3a3c7 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -3887,6 +3887,7 @@ async fn user_turn_updates_approvals_reviewer() { approval_policy: config.permissions.approval_policy.value(), approvals_reviewer: Some(codex_config::types::ApprovalsReviewer::GuardianSubagent), sandbox_policy: config.permissions.sandbox_policy.get().clone(), + permission_profile: None, model: turn_context.model_info.slug.clone(), effort: config.model_reasoning_effort, summary: config.model_reasoning_summary, diff --git a/codex-rs/core/tests/common/test_codex.rs b/codex-rs/core/tests/common/test_codex.rs index 5075f91620..b0be472099 100644 --- a/codex-rs/core/tests/common/test_codex.rs +++ b/codex-rs/core/tests/common/test_codex.rs @@ -625,6 +625,7 @@ impl TestCodex { approval_policy, approvals_reviewer: None, sandbox_policy, + permission_profile: None, model: session_model, effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/apply_patch_cli.rs b/codex-rs/core/tests/suite/apply_patch_cli.rs index 45c97d3704..4872bef88f 100644 --- a/codex-rs/core/tests/suite/apply_patch_cli.rs +++ b/codex-rs/core/tests/suite/apply_patch_cli.rs @@ -366,6 +366,7 @@ async fn apply_patch_cli_move_without_content_change_has_no_turn_diff( approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model, effort: None, summary: None, @@ -1003,6 +1004,7 @@ async fn apply_patch_custom_tool_streaming_emits_updated_changes() -> Result<()> approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: test.session_configured.model.clone(), effort: None, summary: None, @@ -1100,6 +1102,7 @@ async fn apply_patch_shell_command_heredoc_with_cd_emits_turn_diff() -> Result<( approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model, effort: None, summary: None, @@ -1184,6 +1187,7 @@ async fn apply_patch_shell_command_failure_propagates_error_and_skips_diff() -> approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model, effort: None, summary: None, @@ -1339,6 +1343,7 @@ async fn apply_patch_emits_turn_diff_event_with_unified_diff( approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model, effort: None, summary: None, @@ -1406,6 +1411,7 @@ async fn apply_patch_turn_diff_for_rename_with_content_change( approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model, effort: None, summary: None, @@ -1482,6 +1488,7 @@ async fn apply_patch_aggregates_diff_across_multiple_tool_calls() -> Result<()> approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model, effort: None, summary: None, @@ -1558,6 +1565,7 @@ async fn apply_patch_aggregates_diff_preserves_success_after_failure() -> Result approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model, effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/approvals.rs b/codex-rs/core/tests/suite/approvals.rs index f189d5db6c..c393c539a6 100644 --- a/codex-rs/core/tests/suite/approvals.rs +++ b/codex-rs/core/tests/suite/approvals.rs @@ -593,6 +593,7 @@ async fn submit_turn( approval_policy, approvals_reviewer: None, sandbox_policy, + permission_profile: None, model: session_model, effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/client.rs b/codex-rs/core/tests/suite/client.rs index 2086367b21..4433dc9cde 100644 --- a/codex-rs/core/tests/suite/client.rs +++ b/codex-rs/core/tests/suite/client.rs @@ -1646,6 +1646,7 @@ async fn user_turn_collaboration_mode_overrides_model_and_effort() -> anyhow::Re approval_policy: config.permissions.approval_policy.value(), approvals_reviewer: None, sandbox_policy: config.permissions.sandbox_policy.get().clone(), + permission_profile: None, model: session_configured.model.clone(), effort: Some(ReasoningEffort::Low), summary: Some( @@ -1765,6 +1766,7 @@ async fn user_turn_explicit_reasoning_summary_overrides_model_catalog_default() approval_policy: config.permissions.approval_policy.value(), approvals_reviewer: None, sandbox_policy: config.permissions.sandbox_policy.get().clone(), + permission_profile: None, model: session_configured.model, effort: None, summary: Some(ReasoningSummary::Concise), diff --git a/codex-rs/core/tests/suite/code_mode.rs b/codex-rs/core/tests/suite/code_mode.rs index 40a13467e7..55cc2bba12 100644 --- a/codex-rs/core/tests/suite/code_mode.rs +++ b/codex-rs/core/tests/suite/code_mode.rs @@ -2617,6 +2617,7 @@ text( approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: test.session_configured.model.clone(), effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/collaboration_instructions.rs b/codex-rs/core/tests/suite/collaboration_instructions.rs index 28685895ac..69a8b587fc 100644 --- a/codex-rs/core/tests/suite/collaboration_instructions.rs +++ b/codex-rs/core/tests/suite/collaboration_instructions.rs @@ -183,6 +183,7 @@ async fn collaboration_instructions_added_on_user_turn() -> Result<()> { approval_policy: test.config.permissions.approval_policy.value(), approvals_reviewer: None, sandbox_policy: test.config.permissions.sandbox_policy.get().clone(), + permission_profile: None, model: test.session_configured.model.clone(), effort: None, summary: Some( @@ -302,6 +303,7 @@ async fn user_turn_overrides_collaboration_instructions_after_override() -> Resu approval_policy: test.config.permissions.approval_policy.value(), approvals_reviewer: None, sandbox_policy: test.config.permissions.sandbox_policy.get().clone(), + permission_profile: None, model: test.session_configured.model.clone(), effort: None, summary: Some( diff --git a/codex-rs/core/tests/suite/compact.rs b/codex-rs/core/tests/suite/compact.rs index 7a4dc525d9..42a7ab9da0 100644 --- a/codex-rs/core/tests/suite/compact.rs +++ b/codex-rs/core/tests/suite/compact.rs @@ -1674,6 +1674,7 @@ async fn auto_compact_runs_after_resume_when_token_usage_is_over_limit() { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: resumed.session_configured.model.clone(), effort: None, summary: None, @@ -1765,6 +1766,7 @@ async fn pre_sampling_compact_runs_on_switch_to_smaller_context_model() { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: previous_model.to_string(), effort: None, summary: None, @@ -1790,6 +1792,7 @@ async fn pre_sampling_compact_runs_on_switch_to_smaller_context_model() { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: next_model.to_string(), effort: None, summary: None, @@ -1901,6 +1904,7 @@ async fn pre_sampling_compact_runs_after_resume_and_switch_to_smaller_model() { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: previous_model.to_string(), effort: None, summary: None, @@ -1950,6 +1954,7 @@ async fn pre_sampling_compact_runs_after_resume_and_switch_to_smaller_model() { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: next_model.to_string(), effort: None, summary: None, @@ -3172,6 +3177,7 @@ async fn snapshot_request_shape_pre_turn_compaction_strips_incoming_model_switch approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: previous_model.to_string(), effort: None, summary: None, @@ -3197,6 +3203,7 @@ async fn snapshot_request_shape_pre_turn_compaction_strips_incoming_model_switch approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: next_model.to_string(), effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/exec_policy.rs b/codex-rs/core/tests/suite/exec_policy.rs index 3c80fc80d0..d2d3339587 100644 --- a/codex-rs/core/tests/suite/exec_policy.rs +++ b/codex-rs/core/tests/suite/exec_policy.rs @@ -53,6 +53,7 @@ async fn submit_user_turn( approval_policy, approvals_reviewer: None, sandbox_policy, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -134,6 +135,7 @@ async fn execpolicy_blocks_shell_invocation() -> Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/image_rollout.rs b/codex-rs/core/tests/suite/image_rollout.rs index a7ec8318f1..0376fa600f 100644 --- a/codex-rs/core/tests/suite/image_rollout.rs +++ b/codex-rs/core/tests/suite/image_rollout.rs @@ -125,6 +125,7 @@ async fn copy_paste_local_image_persists_rollout_request_shape() -> anyhow::Resu approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -212,6 +213,7 @@ async fn drag_drop_image_persists_rollout_request_shape() -> anyhow::Result<()> approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/items.rs b/codex-rs/core/tests/suite/items.rs index 25eb21df9e..93e88cbc0f 100644 --- a/codex-rs/core/tests/suite/items.rs +++ b/codex-rs/core/tests/suite/items.rs @@ -532,6 +532,7 @@ async fn plan_mode_emits_plan_item_from_proposed_plan_block() -> anyhow::Result< approval_policy: codex_protocol::protocol::AskForApproval::Never, approvals_reviewer: None, sandbox_policy: codex_protocol::protocol::SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_configured.model.clone(), effort: None, summary: None, @@ -609,6 +610,7 @@ async fn plan_mode_strips_plan_from_agent_messages() -> anyhow::Result<()> { approval_policy: codex_protocol::protocol::AskForApproval::Never, approvals_reviewer: None, sandbox_policy: codex_protocol::protocol::SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_configured.model.clone(), effort: None, summary: None, @@ -718,6 +720,7 @@ async fn plan_mode_streaming_citations_are_stripped_across_added_deltas_and_done approval_policy: codex_protocol::protocol::AskForApproval::Never, approvals_reviewer: None, sandbox_policy: codex_protocol::protocol::SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_configured.model.clone(), effort: None, summary: None, @@ -905,6 +908,7 @@ async fn plan_mode_streaming_proposed_plan_tag_split_across_added_and_delta_is_p approval_policy: codex_protocol::protocol::AskForApproval::Never, approvals_reviewer: None, sandbox_policy: codex_protocol::protocol::SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_configured.model.clone(), effort: None, summary: None, @@ -1019,6 +1023,7 @@ async fn plan_mode_handles_missing_plan_close_tag() -> anyhow::Result<()> { approval_policy: codex_protocol::protocol::AskForApproval::Never, approvals_reviewer: None, sandbox_policy: codex_protocol::protocol::SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_configured.model.clone(), effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/json_result.rs b/codex-rs/core/tests/suite/json_result.rs index 755b269472..d09f5ccd24 100644 --- a/codex-rs/core/tests/suite/json_result.rs +++ b/codex-rs/core/tests/suite/json_result.rs @@ -82,6 +82,7 @@ async fn codex_returns_json_result(model: String) -> anyhow::Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model, effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/live_reload.rs b/codex-rs/core/tests/suite/live_reload.rs index 6ab001383f..66eee3f795 100644 --- a/codex-rs/core/tests/suite/live_reload.rs +++ b/codex-rs/core/tests/suite/live_reload.rs @@ -63,6 +63,7 @@ async fn submit_skill_turn(test: &TestCodex, skill_path: PathBuf, prompt: &str) approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/model_switching.rs b/codex-rs/core/tests/suite/model_switching.rs index 5bea4de2a9..a883c7f815 100644 --- a/codex-rs/core/tests/suite/model_switching.rs +++ b/codex-rs/core/tests/suite/model_switching.rs @@ -130,6 +130,7 @@ async fn model_change_appends_model_instructions_developer_message() -> Result<( approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, summary: None, @@ -168,6 +169,7 @@ async fn model_change_appends_model_instructions_developer_message() -> Result<( approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: next_model.to_string(), effort: test.config.model_reasoning_effort, summary: None, @@ -228,6 +230,7 @@ async fn model_and_personality_change_only_appends_model_instructions() -> Resul approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, summary: None, @@ -266,6 +269,7 @@ async fn model_and_personality_change_only_appends_model_instructions() -> Resul approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: next_model.to_string(), effort: test.config.model_reasoning_effort, summary: None, @@ -408,6 +412,7 @@ async fn model_change_from_image_to_text_strips_prior_image_content() -> Result< approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: image_model_slug.to_string(), effort: test.config.model_reasoning_effort, summary: None, @@ -429,6 +434,7 @@ async fn model_change_from_image_to_text_strips_prior_image_content() -> Result< approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: text_model_slug.to_string(), effort: test.config.model_reasoning_effort, summary: None, @@ -537,6 +543,7 @@ async fn generated_image_is_replayed_for_image_capable_models() -> Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: image_model_slug.to_string(), effort: test.config.model_reasoning_effort, service_tier: None, @@ -558,6 +565,7 @@ async fn generated_image_is_replayed_for_image_capable_models() -> Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: image_model_slug.to_string(), effort: test.config.model_reasoning_effort, service_tier: None, @@ -669,6 +677,7 @@ async fn model_change_from_generated_image_to_text_preserves_prior_generated_ima approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: image_model_slug.to_string(), effort: test.config.model_reasoning_effort, service_tier: None, @@ -690,6 +699,7 @@ async fn model_change_from_generated_image_to_text_preserves_prior_generated_ima approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: text_model_slug.to_string(), effort: test.config.model_reasoning_effort, service_tier: None, @@ -803,6 +813,7 @@ async fn thread_rollback_after_generated_image_drops_entire_image_turn_history() approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: image_model_slug.to_string(), effort: test.config.model_reasoning_effort, service_tier: None, @@ -832,6 +843,7 @@ async fn thread_rollback_after_generated_image_drops_entire_image_turn_history() approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: image_model_slug.to_string(), effort: test.config.model_reasoning_effort, service_tier: None, @@ -989,6 +1001,7 @@ async fn model_switch_to_smaller_model_updates_token_context_window() -> Result< approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: large_model_slug.to_string(), effort: test.config.model_reasoning_effort, summary: None, @@ -1049,6 +1062,7 @@ async fn model_switch_to_smaller_model_updates_token_context_window() -> Result< approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: smaller_model_slug.to_string(), effort: test.config.model_reasoning_effort, summary: None, diff --git a/codex-rs/core/tests/suite/model_visible_layout.rs b/codex-rs/core/tests/suite/model_visible_layout.rs index bee3f3245b..84cfaf023a 100644 --- a/codex-rs/core/tests/suite/model_visible_layout.rs +++ b/codex-rs/core/tests/suite/model_visible_layout.rs @@ -123,6 +123,7 @@ async fn snapshot_model_visible_layout_turn_overrides() -> Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, summary: None, @@ -147,6 +148,7 @@ async fn snapshot_model_visible_layout_turn_overrides() -> Result<()> { approval_policy: AskForApproval::OnRequest, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, summary: None, @@ -226,6 +228,7 @@ async fn snapshot_model_visible_layout_cwd_change_does_not_refresh_agents() -> R approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, summary: None, @@ -250,6 +253,7 @@ async fn snapshot_model_visible_layout_cwd_change_does_not_refresh_agents() -> R approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, summary: None, @@ -361,6 +365,7 @@ async fn snapshot_model_visible_layout_resume_with_personality_change() -> Resul approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: resumed.session_configured.model.clone(), effort: resumed.config.model_reasoning_effort, summary: None, diff --git a/codex-rs/core/tests/suite/models_cache_ttl.rs b/codex-rs/core/tests/suite/models_cache_ttl.rs index 2ffce57421..c723055dce 100644 --- a/codex-rs/core/tests/suite/models_cache_ttl.rs +++ b/codex-rs/core/tests/suite/models_cache_ttl.rs @@ -99,6 +99,7 @@ async fn renews_cache_ttl_on_matching_models_etag() -> Result<()> { approval_policy: codex_protocol::protocol::AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: test.session_configured.model.clone(), effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/models_etag_responses.rs b/codex-rs/core/tests/suite/models_etag_responses.rs index 34aaf86dee..dabc9af5a9 100644 --- a/codex-rs/core/tests/suite/models_etag_responses.rs +++ b/codex-rs/core/tests/suite/models_etag_responses.rs @@ -110,6 +110,7 @@ async fn refresh_models_on_models_etag_mismatch_and_avoid_duplicate_models_fetch approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/pending_input.rs b/codex-rs/core/tests/suite/pending_input.rs index 777103534b..a71b6732a6 100644 --- a/codex-rs/core/tests/suite/pending_input.rs +++ b/codex-rs/core/tests/suite/pending_input.rs @@ -118,6 +118,7 @@ async fn submit_danger_full_access_user_turn(test: &TestCodex, text: &str) { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: test.session_configured.model.clone(), effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/personality.rs b/codex-rs/core/tests/suite/personality.rs index 7e729ef493..26c91fccf6 100644 --- a/codex-rs/core/tests/suite/personality.rs +++ b/codex-rs/core/tests/suite/personality.rs @@ -105,6 +105,7 @@ async fn user_turn_personality_none_does_not_add_update_message() -> anyhow::Res approval_policy: test.config.permissions.approval_policy.value(), approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, summary: None, @@ -156,6 +157,7 @@ async fn config_personality_some_sets_instructions_template() -> anyhow::Result< approval_policy: test.config.permissions.approval_policy.value(), approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, summary: None, @@ -214,6 +216,7 @@ async fn config_personality_none_sends_no_personality() -> anyhow::Result<()> { approval_policy: test.config.permissions.approval_policy.value(), approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, summary: None, @@ -278,6 +281,7 @@ async fn default_personality_is_pragmatic_without_config_toml() -> anyhow::Resul approval_policy: test.config.permissions.approval_policy.value(), approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, summary: None, @@ -330,6 +334,7 @@ async fn user_turn_personality_some_adds_update_message() -> anyhow::Result<()> approval_policy: test.config.permissions.approval_policy.value(), approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, summary: None, @@ -369,6 +374,7 @@ async fn user_turn_personality_some_adds_update_message() -> anyhow::Result<()> approval_policy: test.config.permissions.approval_policy.value(), approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, summary: None, @@ -436,6 +442,7 @@ async fn user_turn_personality_same_value_does_not_add_update_message() -> anyho approval_policy: test.config.permissions.approval_policy.value(), approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, summary: None, @@ -475,6 +482,7 @@ async fn user_turn_personality_same_value_does_not_add_update_message() -> anyho approval_policy: test.config.permissions.approval_policy.value(), approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, summary: None, @@ -555,6 +563,7 @@ async fn user_turn_personality_skips_if_feature_disabled() -> anyhow::Result<()> approval_policy: test.config.permissions.approval_policy.value(), approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, summary: None, @@ -594,6 +603,7 @@ async fn user_turn_personality_skips_if_feature_disabled() -> anyhow::Result<()> approval_policy: test.config.permissions.approval_policy.value(), approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: test.session_configured.model.clone(), effort: test.config.model_reasoning_effort, summary: None, @@ -713,6 +723,7 @@ async fn remote_model_friendly_personality_instructions_with_feature() -> anyhow approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: remote_slug.to_string(), effort: test.config.model_reasoning_effort, summary: None, @@ -834,6 +845,7 @@ async fn user_turn_personality_remote_model_template_includes_update_message() - approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: remote_slug.to_string(), effort: test.config.model_reasoning_effort, summary: None, @@ -873,6 +885,7 @@ async fn user_turn_personality_remote_model_template_includes_update_message() - approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: remote_slug.to_string(), effort: test.config.model_reasoning_effort, summary: None, diff --git a/codex-rs/core/tests/suite/prompt_caching.rs b/codex-rs/core/tests/suite/prompt_caching.rs index 80dec74c3b..8d097d0a51 100644 --- a/codex-rs/core/tests/suite/prompt_caching.rs +++ b/codex-rs/core/tests/suite/prompt_caching.rs @@ -717,6 +717,7 @@ async fn per_turn_overrides_keep_cached_prefix_and_key_constant() -> anyhow::Res approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: new_policy.clone(), + permission_profile: None, model: "o3".to_string(), effort: Some(ReasoningEffort::High), summary: Some(ReasoningSummary::Detailed), @@ -830,6 +831,7 @@ async fn send_user_turn_with_no_changes_does_not_send_environment_context() -> a approval_policy: default_approval_policy, approvals_reviewer: None, sandbox_policy: default_sandbox_policy.clone(), + permission_profile: None, model: default_model.clone(), effort: default_effort, summary: Some(default_summary.unwrap_or(ReasoningSummary::Auto)), @@ -851,6 +853,7 @@ async fn send_user_turn_with_no_changes_does_not_send_environment_context() -> a approval_policy: default_approval_policy, approvals_reviewer: None, sandbox_policy: default_sandbox_policy.clone(), + permission_profile: None, model: default_model.clone(), effort: default_effort, summary: Some(default_summary.unwrap_or(ReasoningSummary::Auto)), @@ -956,6 +959,7 @@ async fn send_user_turn_with_changes_sends_environment_context() -> anyhow::Resu approval_policy: default_approval_policy, approvals_reviewer: None, sandbox_policy: default_sandbox_policy.clone(), + permission_profile: None, model: default_model, effort: default_effort, summary: Some(default_summary.unwrap_or(ReasoningSummary::Auto)), @@ -977,6 +981,7 @@ async fn send_user_turn_with_changes_sends_environment_context() -> anyhow::Resu approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: "o3".to_string(), effort: Some(ReasoningEffort::High), summary: Some(ReasoningSummary::Detailed), diff --git a/codex-rs/core/tests/suite/remote_models.rs b/codex-rs/core/tests/suite/remote_models.rs index 42b655aaa9..5818331192 100644 --- a/codex-rs/core/tests/suite/remote_models.rs +++ b/codex-rs/core/tests/suite/remote_models.rs @@ -171,6 +171,7 @@ async fn remote_models_config_context_window_override_clamps_to_max_context_wind summary: None, service_tier: None, collaboration_mode: None, + permission_profile: None, personality: None, }) .await?; @@ -247,6 +248,7 @@ async fn remote_models_config_override_above_max_uses_max_context_window() -> Re summary: None, service_tier: None, collaboration_mode: None, + permission_profile: None, personality: None, }) .await?; @@ -322,6 +324,7 @@ async fn remote_models_use_context_window_when_config_override_is_absent() -> Re summary: None, service_tier: None, collaboration_mode: None, + permission_profile: None, personality: None, }) .await?; @@ -405,6 +408,7 @@ async fn remote_models_long_model_slug_is_sent_with_high_reasoning() -> Result<( approval_policy: config.permissions.approval_policy.value(), approvals_reviewer: None, sandbox_policy: config.permissions.sandbox_policy.get().clone(), + permission_profile: None, model: requested_model.to_string(), effort: None, summary: None, @@ -464,6 +468,7 @@ async fn namespaced_model_slug_uses_catalog_metadata_without_fallback_warning() approval_policy: config.permissions.approval_policy.value(), approvals_reviewer: None, sandbox_policy: config.permissions.sandbox_policy.get().clone(), + permission_profile: None, model: requested_model.to_string(), effort: None, summary: Some( @@ -631,6 +636,7 @@ async fn remote_models_remote_model_uses_unified_exec() -> Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: REMOTE_MODEL_SLUG.to_string(), effort: None, summary: Some(ReasoningSummary::Auto), @@ -857,6 +863,7 @@ async fn remote_models_apply_remote_base_instructions() -> Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: model.to_string(), effort: None, summary: Some(ReasoningSummary::Auto), diff --git a/codex-rs/core/tests/suite/request_permissions.rs b/codex-rs/core/tests/suite/request_permissions.rs index 614cd10b9f..96394c4b06 100644 --- a/codex-rs/core/tests/suite/request_permissions.rs +++ b/codex-rs/core/tests/suite/request_permissions.rs @@ -196,6 +196,7 @@ async fn submit_turn( approval_policy, approvals_reviewer: Some(ApprovalsReviewer::User), sandbox_policy, + permission_profile: None, model: session_model, effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/request_permissions_tool.rs b/codex-rs/core/tests/suite/request_permissions_tool.rs index 0578441e99..d8951f0fdc 100644 --- a/codex-rs/core/tests/suite/request_permissions_tool.rs +++ b/codex-rs/core/tests/suite/request_permissions_tool.rs @@ -147,6 +147,7 @@ async fn submit_turn( approval_policy, approvals_reviewer: None, sandbox_policy, + permission_profile: None, model: session_model, effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/request_user_input.rs b/codex-rs/core/tests/suite/request_user_input.rs index 8e30b37c21..7b6a0680a7 100644 --- a/codex-rs/core/tests/suite/request_user_input.rs +++ b/codex-rs/core/tests/suite/request_user_input.rs @@ -140,6 +140,7 @@ async fn request_user_input_round_trip_for_mode(mode: ModeKind) -> anyhow::Resul approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -258,6 +259,7 @@ where approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/responses_api_proxy_headers.rs b/codex-rs/core/tests/suite/responses_api_proxy_headers.rs index 67f6e86ab0..b72e191850 100644 --- a/codex-rs/core/tests/suite/responses_api_proxy_headers.rs +++ b/codex-rs/core/tests/suite/responses_api_proxy_headers.rs @@ -143,6 +143,7 @@ async fn submit_turn_with_timeout(test: &TestCodex, prompt: &str) -> Result<()> exclude_tmpdir_env_var: false, exclude_slash_tmp: false, }, + permission_profile: None, model: session_model, effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/rmcp_client.rs b/codex-rs/core/tests/suite/rmcp_client.rs index 7875f09810..faa4dcb7cb 100644 --- a/codex-rs/core/tests/suite/rmcp_client.rs +++ b/codex-rs/core/tests/suite/rmcp_client.rs @@ -423,6 +423,7 @@ async fn stdio_server_round_trip() -> anyhow::Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: session_model, effort: None, summary: None, @@ -791,6 +792,7 @@ async fn stdio_mcp_parallel_tool_calls_default_false_runs_serially() -> anyhow:: approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: session_model, effort: None, summary: None, @@ -923,6 +925,7 @@ async fn stdio_mcp_parallel_tool_calls_opt_in_runs_concurrently() -> anyhow::Res approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: session_model, effort: None, summary: None, @@ -1022,6 +1025,7 @@ async fn stdio_image_responses_round_trip() -> anyhow::Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: session_model, effort: None, summary: None, @@ -1173,6 +1177,7 @@ async fn stdio_image_responses_preserve_original_detail_metadata() -> anyhow::Re approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: session_model, effort: None, summary: None, @@ -1410,6 +1415,7 @@ async fn stdio_image_responses_are_sanitized_for_text_only_model() -> anyhow::Re approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: text_only_model_slug.to_string(), effort: None, summary: None, @@ -1518,6 +1524,7 @@ async fn stdio_server_propagates_whitelisted_env_vars() -> anyhow::Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: session_model, effort: None, summary: None, @@ -1887,6 +1894,7 @@ async fn streamable_http_tool_call_round_trip() -> anyhow::Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: session_model, effort: None, summary: None, @@ -2103,6 +2111,7 @@ async fn streamable_http_with_oauth_round_trip_impl() -> anyhow::Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: session_model, effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/safety_check_downgrade.rs b/codex-rs/core/tests/suite/safety_check_downgrade.rs index 51a88ef16a..3c6ca51938 100644 --- a/codex-rs/core/tests/suite/safety_check_downgrade.rs +++ b/codex-rs/core/tests/suite/safety_check_downgrade.rs @@ -47,6 +47,7 @@ async fn openai_model_header_mismatch_emits_warning_event_and_warning_item() -> approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: REQUESTED_MODEL.to_string(), effort: test.config.model_reasoning_effort, summary: None, @@ -146,6 +147,7 @@ async fn response_model_field_mismatch_emits_warning_when_header_matches_request approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: REQUESTED_MODEL.to_string(), effort: test.config.model_reasoning_effort, summary: None, @@ -232,6 +234,7 @@ async fn openai_model_header_mismatch_only_emits_one_warning_per_turn() -> Resul approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: REQUESTED_MODEL.to_string(), effort: test.config.model_reasoning_effort, summary: None, @@ -282,6 +285,7 @@ async fn openai_model_header_casing_only_mismatch_does_not_warn() -> Result<()> approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: REQUESTED_MODEL.to_string(), effort: test.config.model_reasoning_effort, summary: None, diff --git a/codex-rs/core/tests/suite/shell_snapshot.rs b/codex-rs/core/tests/suite/shell_snapshot.rs index 516bc049f5..a6399c3c00 100644 --- a/codex-rs/core/tests/suite/shell_snapshot.rs +++ b/codex-rs/core/tests/suite/shell_snapshot.rs @@ -166,6 +166,7 @@ async fn run_snapshot_command_with_options( approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -257,6 +258,7 @@ async fn run_shell_command_snapshot_with_options( approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -328,6 +330,7 @@ async fn run_tool_turn_on_harness( approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -563,6 +566,7 @@ async fn shell_command_snapshot_still_intercepts_apply_patch() -> Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model, effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/skill_approval.rs b/codex-rs/core/tests/suite/skill_approval.rs index d77d736f94..4aeb9dfdc1 100644 --- a/codex-rs/core/tests/suite/skill_approval.rs +++ b/codex-rs/core/tests/suite/skill_approval.rs @@ -53,6 +53,7 @@ async fn submit_turn_with_policies( approval_policy, approvals_reviewer: None, sandbox_policy, + permission_profile: None, model: test.session_configured.model.clone(), effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/skills.rs b/codex-rs/core/tests/suite/skills.rs index 1b2ac71643..90919bf01e 100644 --- a/codex-rs/core/tests/suite/skills.rs +++ b/codex-rs/core/tests/suite/skills.rs @@ -113,6 +113,7 @@ async fn user_turn_includes_skill_instructions() -> Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/sqlite_state.rs b/codex-rs/core/tests/suite/sqlite_state.rs index e7125210b2..06e8452393 100644 --- a/codex-rs/core/tests/suite/sqlite_state.rs +++ b/codex-rs/core/tests/suite/sqlite_state.rs @@ -406,6 +406,7 @@ async fn mcp_call_marks_thread_memory_mode_polluted_when_configured() -> Result< approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: test.session_configured.model.clone(), effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/tool_harness.rs b/codex-rs/core/tests/suite/tool_harness.rs index 67a7275693..bc6a27bb77 100644 --- a/codex-rs/core/tests/suite/tool_harness.rs +++ b/codex-rs/core/tests/suite/tool_harness.rs @@ -87,6 +87,7 @@ async fn shell_tool_executes_command_and_streams_output() -> anyhow::Result<()> approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -158,6 +159,7 @@ async fn update_plan_tool_emits_plan_update_event() -> anyhow::Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -239,6 +241,7 @@ async fn update_plan_tool_rejects_malformed_payload() -> anyhow::Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -335,6 +338,7 @@ async fn apply_patch_tool_executes_and_emits_patch_events() -> anyhow::Result<() approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -439,6 +443,7 @@ async fn apply_patch_reports_parse_diagnostics() -> anyhow::Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/tool_parallelism.rs b/codex-rs/core/tests/suite/tool_parallelism.rs index 2628136a15..80011f0438 100644 --- a/codex-rs/core/tests/suite/tool_parallelism.rs +++ b/codex-rs/core/tests/suite/tool_parallelism.rs @@ -44,6 +44,7 @@ async fn run_turn(test: &TestCodex, prompt: &str) -> anyhow::Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -361,6 +362,7 @@ async fn shell_tools_start_before_response_completed_when_stream_delayed() -> an approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/truncation.rs b/codex-rs/core/tests/suite/truncation.rs index c12dd58191..9c6d7cb5f2 100644 --- a/codex-rs/core/tests/suite/truncation.rs +++ b/codex-rs/core/tests/suite/truncation.rs @@ -509,6 +509,7 @@ async fn mcp_image_output_preserves_image_and_no_text_summary() -> Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: session_model, effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/unified_exec.rs b/codex-rs/core/tests/suite/unified_exec.rs index be6fa73a40..87ba3474a6 100644 --- a/codex-rs/core/tests/suite/unified_exec.rs +++ b/codex-rs/core/tests/suite/unified_exec.rs @@ -175,6 +175,7 @@ async fn submit_unified_exec_turn( approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -259,6 +260,7 @@ async fn unified_exec_intercepts_apply_patch_exec_command() -> Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -1749,6 +1751,7 @@ async fn unified_exec_keeps_long_running_session_after_turn_end() -> Result<()> approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -1842,6 +1845,7 @@ async fn unified_exec_interrupt_preserves_long_running_session() -> Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -2315,6 +2319,7 @@ async fn unified_exec_runs_under_sandbox() -> Result<()> { approvals_reviewer: None, // Important! sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: session_model, effort: None, summary: None, @@ -2423,6 +2428,7 @@ async fn unified_exec_enforces_glob_deny_read_policy() -> Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: read_only_policy, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -2551,6 +2557,7 @@ async fn unified_exec_python_prompt_under_seatbelt() -> Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: None, model: session_model, effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/user_shell_cmd.rs b/codex-rs/core/tests/suite/user_shell_cmd.rs index 807c746905..9aaee17e38 100644 --- a/codex-rs/core/tests/suite/user_shell_cmd.rs +++ b/codex-rs/core/tests/suite/user_shell_cmd.rs @@ -179,6 +179,7 @@ async fn user_shell_command_does_not_replace_active_turn() -> anyhow::Result<()> approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: fixture.session_configured.model.clone(), effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/view_image.rs b/codex-rs/core/tests/suite/view_image.rs index 9f53e60d72..a867a3dfb5 100644 --- a/codex-rs/core/tests/suite/view_image.rs +++ b/codex-rs/core/tests/suite/view_image.rs @@ -168,6 +168,7 @@ async fn assert_user_turn_local_image_resizes_to( approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -288,6 +289,7 @@ async fn view_image_tool_attaches_local_image() -> anyhow::Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -419,6 +421,7 @@ async fn view_image_tool_can_preserve_original_resolution_when_requested_on_gpt5 approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, service_tier: None, @@ -518,6 +521,7 @@ async fn view_image_tool_errors_clearly_for_unsupported_detail_values() -> anyho approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, service_tier: None, @@ -608,6 +612,7 @@ async fn view_image_tool_treats_null_detail_as_omitted() -> anyhow::Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, service_tier: None, @@ -708,6 +713,7 @@ async fn view_image_tool_resizes_when_model_lacks_original_detail_support() -> a approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, service_tier: None, @@ -812,6 +818,7 @@ async fn view_image_tool_does_not_force_original_resolution_with_capability_only approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, service_tier: None, @@ -914,6 +921,7 @@ await codex.emitImage(out); approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -1034,6 +1042,7 @@ console.log(out.type); approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, service_tier: None, @@ -1127,6 +1136,7 @@ async fn view_image_tool_errors_when_path_is_directory() -> anyhow::Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -1208,6 +1218,7 @@ async fn view_image_tool_errors_for_non_image_files() -> anyhow::Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -1294,6 +1305,7 @@ async fn view_image_tool_errors_when_file_missing() -> anyhow::Result<()> { approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, @@ -1431,6 +1443,7 @@ async fn view_image_tool_returns_unsupported_message_for_text_only_model() -> an approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: model_slug.to_string(), effort: None, summary: None, @@ -1511,6 +1524,7 @@ async fn replaces_invalid_local_image_after_bad_request() -> anyhow::Result<()> approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_model, effort: None, summary: None, diff --git a/codex-rs/core/tests/suite/websocket_fallback.rs b/codex-rs/core/tests/suite/websocket_fallback.rs index c55e72ec64..64ff5e125a 100644 --- a/codex-rs/core/tests/suite/websocket_fallback.rs +++ b/codex-rs/core/tests/suite/websocket_fallback.rs @@ -159,6 +159,7 @@ async fn websocket_fallback_hides_first_websocket_retry_stream_error() -> Result approval_policy: AskForApproval::Never, approvals_reviewer: None, sandbox_policy: SandboxPolicy::DangerFullAccess, + permission_profile: None, model: session_configured.model.clone(), effort: None, summary: None, diff --git a/codex-rs/protocol/src/protocol.rs b/codex-rs/protocol/src/protocol.rs index 5425366d7a..7594ffb1e8 100644 --- a/codex-rs/protocol/src/protocol.rs +++ b/codex-rs/protocol/src/protocol.rs @@ -455,6 +455,13 @@ pub enum Op { /// Policy to use for tool calls such as `local_shell`. sandbox_policy: SandboxPolicy, + /// Full permissions profile to use for tool calls such as `local_shell`. + /// + /// When omitted, `sandbox_policy` is used as a legacy compatibility + /// projection. + #[serde(default, skip_serializing_if = "Option::is_none")] + permission_profile: Option, + /// Must be a valid model slug for the configured client session /// associated with this conversation. model: String, diff --git a/codex-rs/tui/src/app/thread_routing.rs b/codex-rs/tui/src/app/thread_routing.rs index 11f7638f9d..7b0bb80a3b 100644 --- a/codex-rs/tui/src/app/thread_routing.rs +++ b/codex-rs/tui/src/app/thread_routing.rs @@ -497,6 +497,7 @@ impl App { approval_policy, approvals_reviewer, sandbox_policy, + permission_profile, model, effort, summary, @@ -580,6 +581,7 @@ impl App { approvals_reviewer .unwrap_or(self.chat_widget.config_ref().approvals_reviewer), sandbox_policy.clone(), + permission_profile.clone(), model.to_string(), effort, *summary, diff --git a/codex-rs/tui/src/app_command.rs b/codex-rs/tui/src/app_command.rs index c34df5b765..2860400712 100644 --- a/codex-rs/tui/src/app_command.rs +++ b/codex-rs/tui/src/app_command.rs @@ -8,6 +8,7 @@ use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig; use codex_protocol::config_types::ServiceTier; use codex_protocol::config_types::WindowsSandboxLevel; use codex_protocol::mcp::RequestId as McpRequestId; +use codex_protocol::models::PermissionProfile; use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::ConversationAudioParams; @@ -44,6 +45,7 @@ pub(crate) enum AppCommandView<'a> { approval_policy: AskForApproval, approvals_reviewer: &'a Option, sandbox_policy: &'a SandboxPolicy, + permission_profile: &'a Option, model: &'a str, effort: Option, summary: &'a Option, @@ -140,6 +142,7 @@ impl AppCommand { cwd: PathBuf, approval_policy: AskForApproval, sandbox_policy: SandboxPolicy, + permission_profile: Option, model: String, effort: Option, summary: Option, @@ -154,6 +157,7 @@ impl AppCommand { approval_policy, approvals_reviewer: None, sandbox_policy, + permission_profile, model, effort, summary, @@ -290,6 +294,7 @@ impl AppCommand { approval_policy, approvals_reviewer, sandbox_policy, + permission_profile, model, effort, summary, @@ -303,6 +308,7 @@ impl AppCommand { approval_policy: *approval_policy, approvals_reviewer, sandbox_policy, + permission_profile, model, effort: *effort, summary, diff --git a/codex-rs/tui/src/app_server_session.rs b/codex-rs/tui/src/app_server_session.rs index 80c1a7494a..3b73eb36a0 100644 --- a/codex-rs/tui/src/app_server_session.rs +++ b/codex-rs/tui/src/app_server_session.rs @@ -518,6 +518,7 @@ impl AppServerSession { approval_policy: AskForApproval, approvals_reviewer: codex_protocol::config_types::ApprovalsReviewer, sandbox_policy: SandboxPolicy, + permission_profile: Option, model: String, effort: Option, summary: Option, @@ -527,14 +528,19 @@ impl AppServerSession { output_schema: Option, ) -> Result { let request_id = self.next_request_id(); - let (sandbox_policy, permission_profile) = match sandbox_policy { - policy @ SandboxPolicy::ExternalSandbox { .. } => (Some(policy.into()), None), - policy => { - let permission_profile = - PermissionProfile::from_legacy_sandbox_policy(&policy, &cwd); + let (sandbox_policy, permission_profile) = + if let Some(permission_profile) = permission_profile { (None, Some(permission_profile.into())) - } - }; + } else { + match sandbox_policy { + policy @ SandboxPolicy::ExternalSandbox { .. } => (Some(policy.into()), None), + policy => { + let permission_profile = + PermissionProfile::from_legacy_sandbox_policy(&policy, &cwd); + (None, Some(permission_profile.into())) + } + } + }; self.client .request_typed(ClientRequest::TurnStart { request_id, diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 66942b557a..dd36d3e350 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -5936,11 +5936,20 @@ impl ChatWidget { .filter(|_| self.config.features.enabled(Feature::Personality)) .filter(|_| self.current_model_supports_personality()); let service_tier = Some(self.config.service_tier); + let permission_profile = if matches!( + self.config.permissions.sandbox_policy.get(), + SandboxPolicy::ExternalSandbox { .. } + ) { + None + } else { + Some(self.config.permissions.permission_profile()) + }; let op = AppCommand::user_turn( items, self.config.cwd.to_path_buf(), self.config.permissions.approval_policy.value(), self.config.permissions.sandbox_policy.get().clone(), + permission_profile, effective_mode.model().to_string(), effective_mode.reasoning_effort(), /*summary*/ None, diff --git a/codex-rs/tui/src/chatwidget/tests/composer_submission.rs b/codex-rs/tui/src/chatwidget/tests/composer_submission.rs index d35c7e9ded..a23f137b40 100644 --- a/codex-rs/tui/src/chatwidget/tests/composer_submission.rs +++ b/codex-rs/tui/src/chatwidget/tests/composer_submission.rs @@ -86,6 +86,75 @@ async fn submission_preserves_text_elements_and_local_images() { assert!(stored_remote_image_urls.is_empty()); } +#[tokio::test] +async fn submission_includes_configured_permission_profile() { + let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(/*model_override*/ None).await; + + let conversation_id = ThreadId::new(); + let rollout_file = NamedTempFile::new().unwrap(); + let expected_permission_profile = PermissionProfile { + network: Some(NetworkPermissions { + enabled: Some(false), + }), + file_system: Some(FileSystemPermissions { + entries: vec![ + codex_protocol::permissions::FileSystemSandboxEntry { + path: codex_protocol::permissions::FileSystemPath::Special { + value: codex_protocol::permissions::FileSystemSpecialPath::Root, + }, + access: codex_protocol::permissions::FileSystemAccessMode::Read, + }, + codex_protocol::permissions::FileSystemSandboxEntry { + path: codex_protocol::permissions::FileSystemPath::GlobPattern { + pattern: "/home/user/project/secrets/**".to_string(), + }, + access: codex_protocol::permissions::FileSystemAccessMode::None, + }, + ], + glob_scan_max_depth: None, + }), + }; + let configured = codex_protocol::protocol::SessionConfiguredEvent { + session_id: conversation_id, + forked_from_id: None, + thread_name: None, + model: "test-model".to_string(), + model_provider_id: "test-provider".to_string(), + service_tier: None, + approval_policy: AskForApproval::Never, + approvals_reviewer: ApprovalsReviewer::User, + sandbox_policy: SandboxPolicy::new_read_only_policy(), + permission_profile: Some(expected_permission_profile.clone()), + cwd: test_path_buf("/home/user/project").abs(), + reasoning_effort: Some(ReasoningEffortConfig::default()), + history_log_id: 0, + history_entry_count: 0, + initial_messages: None, + network_proxy: None, + rollout_path: Some(rollout_file.path().to_path_buf()), + }; + chat.handle_codex_event(Event { + id: "initial".into(), + msg: EventMsg::SessionConfigured(configured), + }); + drain_insert_history(&mut rx); + + chat.bottom_pane.set_composer_text( + "submit with configured permissions".to_string(), + Vec::new(), + Vec::new(), + ); + chat.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE)); + + let permission_profile = match next_submit_op(&mut op_rx) { + Op::UserTurn { + permission_profile, .. + } => permission_profile, + other => panic!("expected Op::UserTurn, got {other:?}"), + }; + assert_eq!(permission_profile, Some(expected_permission_profile)); +} + #[tokio::test] async fn submission_with_remote_and_local_images_keeps_local_placeholder_numbering() { let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(/*model_override*/ None).await;