From 22f47b3285de8cd2affa0a9b6450354795ef38eb Mon Sep 17 00:00:00 2001 From: Charles Cunningham Date: Sun, 25 Jan 2026 17:20:42 -0800 Subject: [PATCH] Fix --- .../app-server-protocol/src/protocol/v2.rs | 2 - codex-rs/app-server/README.md | 5 +- .../app-server/src/codex_message_processor.rs | 52 ++++++++++++------- .../app-server/tests/suite/v2/exec_run.rs | 4 -- 4 files changed, 35 insertions(+), 28 deletions(-) diff --git a/codex-rs/app-server-protocol/src/protocol/v2.rs b/codex-rs/app-server-protocol/src/protocol/v2.rs index 5ad3971a73..2897eb8412 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2.rs @@ -1091,8 +1091,6 @@ pub struct ExecRunParams { pub model: Option, pub model_provider: Option, pub cwd: Option, - pub approval_policy: Option, - pub sandbox_policy: Option, pub effort: Option, pub summary: Option, pub collaboration_mode: Option, diff --git a/codex-rs/app-server/README.md b/codex-rs/app-server/README.md index 1f19e9c09c..ccaf000cb2 100644 --- a/codex-rs/app-server/README.md +++ b/codex-rs/app-server/README.md @@ -380,9 +380,7 @@ Run a single turn to completion without subscribing to streamed turn/item events "type": "text", "text": "Generate a concise thread title", "textElements": [] - }], - "approvalPolicy": "never", // optional - "sandboxPolicy": { "type": "readOnly" } // optional + }] } } { "id": 31, "result": { "threadId": "thread-123", @@ -397,6 +395,7 @@ Notes: - `exec/run` is best for one-off utilities (for example, generating a title) where you just need the final result. - `exec/run` always runs ephemerally, so it does not create a rollout and will not appear in `thread/list`. +- `exec/run` forces `approvalPolicy: "never"` and a read-only sandbox. - Use `turn/start` when you want streaming events, intermediate items, or a long-lived thread. ### Example: One-off command execution diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index 3fe24df3b1..19758e80e3 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -1398,25 +1398,40 @@ impl CodexMessageProcessor { return; } - if let Some(policy) = params.sandbox_policy.as_ref() { - let requested_policy = policy.clone().to_core(); - if let Err(err) = self.config.sandbox_policy.can_set(&requested_policy) { - let error = JSONRPCErrorError { - code: INVALID_REQUEST_ERROR_CODE, - message: format!("invalid sandbox policy: {err}"), - data: None, - }; - self.outgoing.send_error(request_id, error).await; - return; - } + if let Err(err) = self + .config + .approval_policy + .can_set(&codex_protocol::protocol::AskForApproval::Never) + { + let error = JSONRPCErrorError { + code: INVALID_REQUEST_ERROR_CODE, + message: format!("invalid approval policy: {err}"), + data: None, + }; + self.outgoing.send_error(request_id, error).await; + return; + } + + if let Err(err) = self + .config + .sandbox_policy + .can_set(&codex_protocol::protocol::SandboxPolicy::ReadOnly) + { + let error = JSONRPCErrorError { + code: INVALID_REQUEST_ERROR_CODE, + message: format!("invalid sandbox policy: {err}"), + data: None, + }; + self.outgoing.send_error(request_id, error).await; + return; } let mut typesafe_overrides = self.build_thread_config_overrides( params.model, params.model_provider, params.cwd, - params.approval_policy, - None, + Some(codex_app_server_protocol::AskForApproval::Never), + Some(codex_app_server_protocol::SandboxMode::ReadOnly), params.base_instructions, params.developer_instructions, params.personality, @@ -1458,8 +1473,8 @@ impl CodexMessageProcessor { let thread_ids_to_skip_listener_attachment = self.thread_ids_to_skip_listener_attachment.clone(); - let thread_id_for_turn = thread_id.clone(); - let thread_id_for_remove = thread_id.clone(); + let thread_id_for_turn = thread_id; + let thread_id_for_remove = thread_id; thread_ids_to_skip_listener_attachment .lock() .await @@ -1468,17 +1483,16 @@ impl CodexMessageProcessor { let response_result: Result = async { let has_turn_overrides = params.effort.is_some() || params.summary.is_some() - || params.collaboration_mode.is_some() - || params.sandbox_policy.is_some(); + || params.collaboration_mode.is_some(); if has_turn_overrides { let _ = thread .submit(Op::OverrideTurnContext { cwd: None, approval_policy: None, - sandbox_policy: params.sandbox_policy.map(|policy| policy.to_core()), + sandbox_policy: None, model: None, effort: params.effort.map(Some), - summary: params.summary.clone(), + summary: params.summary, collaboration_mode: params.collaboration_mode.clone(), personality: None, }) diff --git a/codex-rs/app-server/tests/suite/v2/exec_run.rs b/codex-rs/app-server/tests/suite/v2/exec_run.rs index 8b4432d668..bc416ba8d6 100644 --- a/codex-rs/app-server/tests/suite/v2/exec_run.rs +++ b/codex-rs/app-server/tests/suite/v2/exec_run.rs @@ -40,8 +40,6 @@ async fn exec_run_completes_turn_and_returns_final_message() -> Result<()> { model: None, model_provider: None, cwd: None, - approval_policy: None, - sandbox_policy: None, effort: None, summary: None, collaboration_mode: None, @@ -88,8 +86,6 @@ async fn exec_run_rejects_empty_input() -> Result<()> { model: None, model_provider: None, cwd: None, - approval_policy: None, - sandbox_policy: None, effort: None, summary: None, collaboration_mode: None,