diff --git a/codex-rs/app-server-protocol/schema/json/ClientRequest.json b/codex-rs/app-server-protocol/schema/json/ClientRequest.json index 5b33c5a8a..03be878ce 100644 --- a/codex-rs/app-server-protocol/schema/json/ClientRequest.json +++ b/codex-rs/app-server-protocol/schema/json/ClientRequest.json @@ -57,6 +57,9 @@ "mcp_elicitations": { "type": "boolean" }, + "request_permissions": { + "type": "boolean" + }, "rules": { "type": "boolean" }, @@ -66,6 +69,7 @@ }, "required": [ "mcp_elicitations", + "request_permissions", "rules", "sandbox_approval" ], diff --git a/codex-rs/app-server-protocol/schema/json/EventMsg.json b/codex-rs/app-server-protocol/schema/json/EventMsg.json index 1a4bac2b0..ea67aec65 100644 --- a/codex-rs/app-server-protocol/schema/json/EventMsg.json +++ b/codex-rs/app-server-protocol/schema/json/EventMsg.json @@ -4683,6 +4683,10 @@ "description": "Reject MCP elicitation prompts.", "type": "boolean" }, + "request_permissions": { + "description": "Reject approval prompts related to built-in permission requests.", + "type": "boolean" + }, "rules": { "description": "Reject prompts triggered by execpolicy `prompt` rules.", "type": "boolean" @@ -4694,6 +4698,7 @@ }, "required": [ "mcp_elicitations", + "request_permissions", "rules", "sandbox_approval" ], diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json index fcc9cb1e9..1b80de6ff 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json @@ -6752,6 +6752,10 @@ "description": "Reject MCP elicitation prompts.", "type": "boolean" }, + "request_permissions": { + "description": "Reject approval prompts related to built-in permission requests.", + "type": "boolean" + }, "rules": { "description": "Reject prompts triggered by execpolicy `prompt` rules.", "type": "boolean" @@ -6763,6 +6767,7 @@ }, "required": [ "mcp_elicitations", + "request_permissions", "rules", "sandbox_approval" ], @@ -9232,6 +9237,9 @@ "mcp_elicitations": { "type": "boolean" }, + "request_permissions": { + "type": "boolean" + }, "rules": { "type": "boolean" }, @@ -9241,6 +9249,7 @@ }, "required": [ "mcp_elicitations", + "request_permissions", "rules", "sandbox_approval" ], diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json index 9493430f3..c608fb8e1 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json @@ -731,6 +731,9 @@ "mcp_elicitations": { "type": "boolean" }, + "request_permissions": { + "type": "boolean" + }, "rules": { "type": "boolean" }, @@ -740,6 +743,7 @@ }, "required": [ "mcp_elicitations", + "request_permissions", "rules", "sandbox_approval" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ConfigReadResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ConfigReadResponse.json index 90828da0b..2a2f0be78 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ConfigReadResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ConfigReadResponse.json @@ -148,6 +148,9 @@ "mcp_elicitations": { "type": "boolean" }, + "request_permissions": { + "type": "boolean" + }, "rules": { "type": "boolean" }, @@ -157,6 +160,7 @@ }, "required": [ "mcp_elicitations", + "request_permissions", "rules", "sandbox_approval" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ConfigRequirementsReadResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ConfigRequirementsReadResponse.json index 55bc3c62c..0d7b24528 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ConfigRequirementsReadResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ConfigRequirementsReadResponse.json @@ -20,6 +20,9 @@ "mcp_elicitations": { "type": "boolean" }, + "request_permissions": { + "type": "boolean" + }, "rules": { "type": "boolean" }, @@ -29,6 +32,7 @@ }, "required": [ "mcp_elicitations", + "request_permissions", "rules", "sandbox_approval" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkParams.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkParams.json index d4ef3effd..92f3a1600 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkParams.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkParams.json @@ -20,6 +20,9 @@ "mcp_elicitations": { "type": "boolean" }, + "request_permissions": { + "type": "boolean" + }, "rules": { "type": "boolean" }, @@ -29,6 +32,7 @@ }, "required": [ "mcp_elicitations", + "request_permissions", "rules", "sandbox_approval" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json index fec4e20e4..c671a7738 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json @@ -24,6 +24,9 @@ "mcp_elicitations": { "type": "boolean" }, + "request_permissions": { + "type": "boolean" + }, "rules": { "type": "boolean" }, @@ -33,6 +36,7 @@ }, "required": [ "mcp_elicitations", + "request_permissions", "rules", "sandbox_approval" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeParams.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeParams.json index 75c8a3967..aec9a6d49 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeParams.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeParams.json @@ -20,6 +20,9 @@ "mcp_elicitations": { "type": "boolean" }, + "request_permissions": { + "type": "boolean" + }, "rules": { "type": "boolean" }, @@ -29,6 +32,7 @@ }, "required": [ "mcp_elicitations", + "request_permissions", "rules", "sandbox_approval" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json index e8addc110..42a931c6e 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json @@ -24,6 +24,9 @@ "mcp_elicitations": { "type": "boolean" }, + "request_permissions": { + "type": "boolean" + }, "rules": { "type": "boolean" }, @@ -33,6 +36,7 @@ }, "required": [ "mcp_elicitations", + "request_permissions", "rules", "sandbox_approval" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartParams.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartParams.json index 0cdc8d459..659fb9f97 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartParams.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartParams.json @@ -20,6 +20,9 @@ "mcp_elicitations": { "type": "boolean" }, + "request_permissions": { + "type": "boolean" + }, "rules": { "type": "boolean" }, @@ -29,6 +32,7 @@ }, "required": [ "mcp_elicitations", + "request_permissions", "rules", "sandbox_approval" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json index 52d9e2951..966ed815d 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json @@ -24,6 +24,9 @@ "mcp_elicitations": { "type": "boolean" }, + "request_permissions": { + "type": "boolean" + }, "rules": { "type": "boolean" }, @@ -33,6 +36,7 @@ }, "required": [ "mcp_elicitations", + "request_permissions", "rules", "sandbox_approval" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/TurnStartParams.json b/codex-rs/app-server-protocol/schema/json/v2/TurnStartParams.json index b1d5febac..7a6184e3c 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/TurnStartParams.json +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnStartParams.json @@ -24,6 +24,9 @@ "mcp_elicitations": { "type": "boolean" }, + "request_permissions": { + "type": "boolean" + }, "rules": { "type": "boolean" }, @@ -33,6 +36,7 @@ }, "required": [ "mcp_elicitations", + "request_permissions", "rules", "sandbox_approval" ], diff --git a/codex-rs/app-server-protocol/schema/typescript/RejectConfig.ts b/codex-rs/app-server-protocol/schema/typescript/RejectConfig.ts index 320f1096b..19b26481c 100644 --- a/codex-rs/app-server-protocol/schema/typescript/RejectConfig.ts +++ b/codex-rs/app-server-protocol/schema/typescript/RejectConfig.ts @@ -11,6 +11,10 @@ sandbox_approval: boolean, * Reject prompts triggered by execpolicy `prompt` rules. */ rules: boolean, +/** + * Reject approval prompts related to built-in permission requests. + */ +request_permissions: boolean, /** * Reject MCP elicitation prompts. */ diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/AskForApproval.ts b/codex-rs/app-server-protocol/schema/typescript/v2/AskForApproval.ts index b5f1bacdf..46f5fa8c3 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/AskForApproval.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/AskForApproval.ts @@ -2,4 +2,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type AskForApproval = "untrusted" | "on-failure" | "on-request" | { "reject": { sandbox_approval: boolean, rules: boolean, mcp_elicitations: boolean, } } | "never"; +export type AskForApproval = "untrusted" | "on-failure" | "on-request" | { "reject": { sandbox_approval: boolean, rules: boolean, request_permissions: boolean, mcp_elicitations: boolean, } } | "never"; diff --git a/codex-rs/app-server-protocol/src/protocol/v2.rs b/codex-rs/app-server-protocol/src/protocol/v2.rs index 756cad2e6..f28c86e53 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2.rs @@ -193,6 +193,7 @@ pub enum AskForApproval { Reject { sandbox_approval: bool, rules: bool, + request_permissions: bool, mcp_elicitations: bool, }, Never, @@ -207,10 +208,12 @@ impl AskForApproval { AskForApproval::Reject { sandbox_approval, rules, + request_permissions, mcp_elicitations, } => CoreAskForApproval::Reject(CoreRejectConfig { sandbox_approval, rules, + request_permissions, mcp_elicitations, }), AskForApproval::Never => CoreAskForApproval::Never, @@ -227,6 +230,7 @@ impl From for AskForApproval { CoreAskForApproval::Reject(reject_config) => AskForApproval::Reject { sandbox_approval: reject_config.sandbox_approval, rules: reject_config.rules, + request_permissions: reject_config.request_permissions, mcp_elicitations: reject_config.mcp_elicitations, }, CoreAskForApproval::Never => AskForApproval::Never, @@ -5832,6 +5836,30 @@ mod tests { assert_eq!(back_to_v2, v2_policy); } + #[test] + fn ask_for_approval_reject_round_trips_request_permissions_flag() { + let v2_policy = AskForApproval::Reject { + sandbox_approval: true, + rules: false, + request_permissions: true, + mcp_elicitations: false, + }; + + let core_policy = v2_policy.to_core(); + assert_eq!( + core_policy, + CoreAskForApproval::Reject(CoreRejectConfig { + sandbox_approval: true, + rules: false, + request_permissions: true, + mcp_elicitations: false, + }) + ); + + let back_to_v2 = AskForApproval::from(core_policy); + assert_eq!(back_to_v2, v2_policy); + } + #[test] fn mcp_server_elicitation_response_round_trips_rmcp_result() { let rmcp_result = rmcp::model::CreateElicitationResult { diff --git a/codex-rs/app-server/README.md b/codex-rs/app-server/README.md index ac0341323..64de7c3f5 100644 --- a/codex-rs/app-server/README.md +++ b/codex-rs/app-server/README.md @@ -928,6 +928,8 @@ Only the granted subset matters on the wire. Any permissions omitted from `resul Within the same turn, granted permissions are sticky: later shell-like tool calls can automatically reuse the granted subset without reissuing a separate permission request. +If the session approval policy uses `Reject` with `request_permissions: true`, the server does not send `item/permissions/requestApproval` to the client. Instead, the tool is auto-denied and resolves with an empty granted-permissions payload. + ### Dynamic tool calls (experimental) `dynamicTools` on `thread/start` and the corresponding `item/tool/call` request/response flow are experimental APIs. To enable them, set `initialize.params.capabilities.experimentalApi = true`. diff --git a/codex-rs/core/config.schema.json b/codex-rs/core/config.schema.json index 7498ffd20..a1ca57d7a 100644 --- a/codex-rs/core/config.schema.json +++ b/codex-rs/core/config.schema.json @@ -1316,6 +1316,10 @@ "description": "Reject MCP elicitation prompts.", "type": "boolean" }, + "request_permissions": { + "description": "Reject approval prompts related to built-in permission requests.", + "type": "boolean" + }, "rules": { "description": "Reject prompts triggered by execpolicy `prompt` rules.", "type": "boolean" @@ -1327,6 +1331,7 @@ }, "required": [ "mcp_elicitations", + "request_permissions", "rules", "sandbox_approval" ], diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index 460eeaa56..52411a4d4 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -2831,6 +2831,25 @@ impl Session { call_id: String, args: RequestPermissionsArgs, ) -> Option { + match turn_context.approval_policy.value() { + AskForApproval::Never => { + return Some(RequestPermissionsResponse { + permissions: PermissionProfile::default(), + }); + } + AskForApproval::Reject(reject_config) + if reject_config.rejects_request_permissions() => + { + return Some(RequestPermissionsResponse { + permissions: PermissionProfile::default(), + }); + } + AskForApproval::OnFailure + | AskForApproval::OnRequest + | AskForApproval::UnlessTrusted + | AskForApproval::Reject(_) => {} + } + let (tx_response, rx_response) = oneshot::channel(); let prev_entry = { let mut active = self.active_turn.lock().await; diff --git a/codex-rs/core/src/codex_tests.rs b/codex-rs/core/src/codex_tests.rs index d045d6753..fc6aab120 100644 --- a/codex-rs/core/src/codex_tests.rs +++ b/codex-rs/core/src/codex_tests.rs @@ -2165,6 +2165,128 @@ async fn notify_request_permissions_response_ignores_unmatched_call_id() { assert_eq!(session.granted_turn_permissions().await, None); } +#[tokio::test] +async fn request_permissions_emits_event_when_reject_policy_allows_requests() { + let (session, mut turn_context, rx) = make_session_and_context_with_rx().await; + *session.active_turn.lock().await = Some(ActiveTurn::default()); + Arc::get_mut(&mut turn_context) + .expect("single turn context ref") + .approval_policy + .set(crate::protocol::AskForApproval::Reject( + crate::protocol::RejectConfig { + sandbox_approval: true, + rules: true, + request_permissions: false, + mcp_elicitations: true, + }, + )) + .expect("test setup should allow updating approval policy"); + + let session = Arc::new(session); + let turn_context = Arc::new(turn_context); + let call_id = "call-1".to_string(); + let expected_response = codex_protocol::request_permissions::RequestPermissionsResponse { + permissions: codex_protocol::models::PermissionProfile { + network: Some(codex_protocol::models::NetworkPermissions { + enabled: Some(true), + }), + ..Default::default() + }, + }; + + let handle = tokio::spawn({ + let session = Arc::clone(&session); + let turn_context = Arc::clone(&turn_context); + let call_id = call_id.clone(); + async move { + session + .request_permissions( + turn_context.as_ref(), + call_id, + codex_protocol::request_permissions::RequestPermissionsArgs { + reason: Some("need network".to_string()), + permissions: codex_protocol::models::PermissionProfile { + network: Some(codex_protocol::models::NetworkPermissions { + enabled: Some(true), + }), + ..Default::default() + }, + }, + ) + .await + } + }); + + let request_event = tokio::time::timeout(StdDuration::from_secs(1), rx.recv()) + .await + .expect("request_permissions event timed out") + .expect("request_permissions event missing"); + let EventMsg::RequestPermissions(request) = request_event.msg else { + panic!("expected request_permissions event"); + }; + assert_eq!(request.call_id, call_id); + + session + .notify_request_permissions_response(&request.call_id, expected_response.clone()) + .await; + + let response = tokio::time::timeout(StdDuration::from_secs(1), handle) + .await + .expect("request_permissions future timed out") + .expect("request_permissions join error"); + + assert_eq!(response, Some(expected_response)); +} + +#[tokio::test] +async fn request_permissions_returns_empty_grant_when_reject_policy_blocks_requests() { + let (session, mut turn_context, rx) = make_session_and_context_with_rx().await; + *session.active_turn.lock().await = Some(ActiveTurn::default()); + Arc::get_mut(&mut turn_context) + .expect("single turn context ref") + .approval_policy + .set(crate::protocol::AskForApproval::Reject( + crate::protocol::RejectConfig { + sandbox_approval: false, + rules: false, + request_permissions: true, + mcp_elicitations: false, + }, + )) + .expect("test setup should allow updating approval policy"); + + let response = session + .request_permissions( + &turn_context, + "call-1".to_string(), + codex_protocol::request_permissions::RequestPermissionsArgs { + reason: Some("need network".to_string()), + permissions: codex_protocol::models::PermissionProfile { + network: Some(codex_protocol::models::NetworkPermissions { + enabled: Some(true), + }), + ..Default::default() + }, + }, + ) + .await; + + assert_eq!( + response, + Some( + codex_protocol::request_permissions::RequestPermissionsResponse { + permissions: codex_protocol::models::PermissionProfile::default(), + } + ) + ); + assert!( + tokio::time::timeout(StdDuration::from_millis(50), rx.recv()) + .await + .is_err(), + "unexpected request_permissions event emitted", + ); +} + #[tokio::test] async fn submit_with_id_captures_current_span_trace_context() { let (session, _turn_context) = make_session_and_context().await; diff --git a/codex-rs/core/src/exec_policy.rs b/codex-rs/core/src/exec_policy.rs index 2a05432ec..60edee651 100644 --- a/codex-rs/core/src/exec_policy.rs +++ b/codex-rs/core/src/exec_policy.rs @@ -1569,6 +1569,7 @@ prefix_rule(pattern=["git"], decision="prompt") AskForApproval::Reject(RejectConfig { sandbox_approval: false, rules: false, + request_permissions: false, mcp_elicitations: false, }), &SandboxPolicy::new_read_only_policy(), @@ -1590,6 +1591,7 @@ prefix_rule(pattern=["git"], decision="prompt") approval_policy: AskForApproval::Reject(RejectConfig { sandbox_approval: true, rules: false, + request_permissions: false, mcp_elicitations: false, }), sandbox_policy: &SandboxPolicy::new_read_only_policy(), @@ -1626,6 +1628,7 @@ prefix_rule(pattern=["git"], decision="prompt") approval_policy: AskForApproval::Reject(RejectConfig { sandbox_approval: true, rules: false, + request_permissions: false, mcp_elicitations: false, }), sandbox_policy: &SandboxPolicy::new_read_only_policy(), @@ -1660,6 +1663,7 @@ prefix_rule(pattern=["git"], decision="prompt") approval_policy: AskForApproval::Reject(RejectConfig { sandbox_approval: false, rules: true, + request_permissions: false, mcp_elicitations: false, }), sandbox_policy: &SandboxPolicy::new_read_only_policy(), diff --git a/codex-rs/core/src/mcp_connection_manager.rs b/codex-rs/core/src/mcp_connection_manager.rs index e339d8722..d133441c3 100644 --- a/codex-rs/core/src/mcp_connection_manager.rs +++ b/codex-rs/core/src/mcp_connection_manager.rs @@ -1739,6 +1739,7 @@ mod tests { RejectConfig { sandbox_approval: false, rules: false, + request_permissions: false, mcp_elicitations: false, } ))); @@ -1751,6 +1752,7 @@ mod tests { RejectConfig { sandbox_approval: false, rules: false, + request_permissions: false, mcp_elicitations: true, } ))); diff --git a/codex-rs/core/src/safety.rs b/codex-rs/core/src/safety.rs index d2e74beed..be32c09cb 100644 --- a/codex-rs/core/src/safety.rs +++ b/codex-rs/core/src/safety.rs @@ -316,6 +316,7 @@ mod tests { AskForApproval::Reject(RejectConfig { sandbox_approval: false, rules: false, + request_permissions: false, mcp_elicitations: false, }), &policy_workspace_only, @@ -348,6 +349,7 @@ mod tests { AskForApproval::Reject(RejectConfig { sandbox_approval: true, rules: false, + request_permissions: false, mcp_elicitations: false, }), &policy_workspace_only, diff --git a/codex-rs/core/src/tools/runtimes/apply_patch.rs b/codex-rs/core/src/tools/runtimes/apply_patch.rs index 8b14dd1af..fd0168bf4 100644 --- a/codex-rs/core/src/tools/runtimes/apply_patch.rs +++ b/codex-rs/core/src/tools/runtimes/apply_patch.rs @@ -218,6 +218,7 @@ mod tests { !runtime.wants_no_sandbox_approval(AskForApproval::Reject(RejectConfig { sandbox_approval: true, rules: false, + request_permissions: false, mcp_elicitations: false, })) ); @@ -225,6 +226,7 @@ mod tests { runtime.wants_no_sandbox_approval(AskForApproval::Reject(RejectConfig { sandbox_approval: false, rules: false, + request_permissions: false, mcp_elicitations: false, })) ); diff --git a/codex-rs/core/src/tools/sandboxing.rs b/codex-rs/core/src/tools/sandboxing.rs index c7883d19b..935b162b2 100644 --- a/codex-rs/core/src/tools/sandboxing.rs +++ b/codex-rs/core/src/tools/sandboxing.rs @@ -398,6 +398,7 @@ mod tests { let policy = AskForApproval::Reject(RejectConfig { sandbox_approval: true, rules: false, + request_permissions: false, mcp_elicitations: false, }); @@ -417,6 +418,7 @@ mod tests { let policy = AskForApproval::Reject(RejectConfig { sandbox_approval: false, rules: true, + request_permissions: false, mcp_elicitations: true, }); diff --git a/codex-rs/core/tests/suite/skill_approval.rs b/codex-rs/core/tests/suite/skill_approval.rs index 6ec6f597f..0c896aaed 100644 --- a/codex-rs/core/tests/suite/skill_approval.rs +++ b/codex-rs/core/tests/suite/skill_approval.rs @@ -288,6 +288,7 @@ async fn shell_zsh_fork_skill_script_reject_policy_with_sandbox_approval_false_s let approval_policy = AskForApproval::Reject(RejectConfig { sandbox_approval: false, rules: true, + request_permissions: false, mcp_elicitations: false, }); let server = start_mock_server().await; @@ -380,6 +381,7 @@ async fn shell_zsh_fork_skill_script_reject_policy_with_sandbox_approval_true_sk let approval_policy = AskForApproval::Reject(RejectConfig { sandbox_approval: true, rules: false, + request_permissions: false, mcp_elicitations: false, }); let server = start_mock_server().await; diff --git a/codex-rs/protocol/src/models.rs b/codex-rs/protocol/src/models.rs index a34eeeb8e..f8948a772 100644 --- a/codex-rs/protocol/src/models.rs +++ b/codex-rs/protocol/src/models.rs @@ -453,12 +453,14 @@ impl DeveloperInstructions { let on_request_instructions = on_request_instructions(); let sandbox_approval = reject_config.sandbox_approval; let rules = reject_config.rules; + let request_permissions = reject_config.request_permissions; let mcp_elicitations = reject_config.mcp_elicitations; format!( "{on_request_instructions}\n\n\ Approval policy is `reject`.\n\ - `sandbox_approval`: {sandbox_approval}\n\ - `rules`: {rules}\n\ + - `request_permissions`: {request_permissions}\n\ - `mcp_elicitations`: {mcp_elicitations}\n\ When a category is `true`, requests in that category are auto-rejected instead of prompting the user." ) diff --git a/codex-rs/protocol/src/protocol.rs b/codex-rs/protocol/src/protocol.rs index 82e5084db..923380199 100644 --- a/codex-rs/protocol/src/protocol.rs +++ b/codex-rs/protocol/src/protocol.rs @@ -526,6 +526,8 @@ pub struct RejectConfig { pub sandbox_approval: bool, /// Reject prompts triggered by execpolicy `prompt` rules. pub rules: bool, + /// Reject approval prompts related to built-in permission requests. + pub request_permissions: bool, /// Reject MCP elicitation prompts. pub mcp_elicitations: bool, } @@ -539,6 +541,10 @@ impl RejectConfig { self.rules } + pub const fn rejects_request_permissions(self) -> bool { + self.request_permissions + } + pub const fn rejects_mcp_elicitations(self) -> bool { self.mcp_elicitations } @@ -3298,6 +3304,7 @@ mod tests { RejectConfig { sandbox_approval: false, rules: false, + request_permissions: false, mcp_elicitations: true, } .rejects_mcp_elicitations() @@ -3306,12 +3313,35 @@ mod tests { !RejectConfig { sandbox_approval: false, rules: false, + request_permissions: false, mcp_elicitations: false, } .rejects_mcp_elicitations() ); } + #[test] + fn reject_config_request_permissions_flag_is_field_driven() { + assert!( + RejectConfig { + sandbox_approval: false, + rules: false, + request_permissions: true, + mcp_elicitations: false, + } + .rejects_request_permissions() + ); + assert!( + !RejectConfig { + sandbox_approval: false, + rules: false, + request_permissions: false, + mcp_elicitations: false, + } + .rejects_request_permissions() + ); + } + #[test] fn workspace_write_restricted_read_access_includes_effective_writable_roots() { let cwd = if cfg!(windows) {