Merge remote-tracking branch 'origin/main' into codex/pr27914-refresh

This commit is contained in:
Chris Bookholt
2026-06-17 11:00:35 -07:00
41 changed files with 216 additions and 14 deletions
+1
View File
@@ -31,6 +31,7 @@ Keep these requirements in mind while migrating code to conform with the above g
* path reasoning must work before the related environment has come online
* URIs cannot explicitly encode the executor’s path convention or operating system
* users must not configure the environment’s OS/path convention explicitly
* URIs should not yet be stored in rollouts
It is OK if the conversion between paths and URIs is somewhat lossy as long as it will do the right
thing for real users.
@@ -861,6 +861,7 @@ fn sample_command_approval_request(request_id: i64, approval_id: Option<&str>) -
item_id: "item-1".to_string(),
started_at_ms: 1_000,
approval_id: approval_id.map(str::to_string),
environment_id: None,
reason: None,
network_approval_context: None,
command: Some("echo hi".to_string()),
@@ -555,6 +555,14 @@
],
"description": "The command's working directory."
},
"environmentId": {
"default": null,
"description": "Environment in which the command will run.",
"type": [
"string",
"null"
]
},
"itemId": {
"type": "string"
},
@@ -379,6 +379,14 @@
],
"description": "The command's working directory."
},
"environmentId": {
"default": null,
"description": "Environment in which the command will run.",
"type": [
"string",
"null"
]
},
"itemId": {
"type": "string"
},
@@ -2386,6 +2386,14 @@
],
"description": "The command's working directory."
},
"environmentId": {
"default": null,
"description": "Environment in which the command will run.",
"type": [
"string",
"null"
]
},
"itemId": {
"type": "string"
},
@@ -20,6 +20,9 @@ startedAtMs: number, /**
* (a UUID) used to disambiguate routing.
*/
approvalId?: string | null, /**
* Environment in which the command will run.
*/
environmentId: string | null, /**
* Optional explanatory reason (e.g. request for network access).
*/
reason?: string | null, /**
@@ -3538,6 +3538,7 @@ mod tests {
item_id: "call_123".to_string(),
started_at_ms: 0,
approval_id: None,
environment_id: None,
reason: None,
network_approval_context: None,
command: Some("cat file".to_string()),
@@ -1321,6 +1321,9 @@ pub struct CommandExecutionRequestApprovalParams {
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional = nullable)]
pub approval_id: Option<String>,
/// Environment in which the command will run.
#[serde(default)]
pub environment_id: Option<String>,
/// Optional explanatory reason (e.g. request for network access).
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional = nullable)]
@@ -2058,6 +2058,7 @@ impl CodexClient {
item_id,
started_at_ms: _,
approval_id,
environment_id,
reason,
network_approval_context,
command,
@@ -2075,6 +2076,9 @@ impl CodexClient {
);
self.command_approval_count += 1;
self.command_approval_item_ids.push(item_id.clone());
if let Some(environment_id) = environment_id.as_deref() {
println!("< environment: {environment_id}");
}
if let Some(reason) = reason.as_deref() {
println!("< reason: {reason}");
}
+1 -1
View File
@@ -1429,7 +1429,7 @@ Certain actions (shell commands or modifying files) may require explicit user ap
Order of messages:
1. `item/started` — shows the pending `commandExecution` item with `command`, `cwd`, and other fields so you can render the proposed action.
2. `item/commandExecution/requestApproval` (request) — carries the same `itemId`, `threadId`, `turnId`, optionally `approvalId` (for subcommand callbacks), and `reason`. For normal command approvals, it also includes `command`, `cwd`, and `commandActions` for friendly display. When `initialize.params.capabilities.experimentalApi = true`, it may also include experimental `additionalPermissions` describing requested per-command sandbox access; any filesystem paths in that payload are absolute on the wire, and network access is represented as `additionalPermissions.network.enabled`. For network-only approvals, those command fields may be omitted and `networkApprovalContext` is provided instead. Optional persistence hints may also be included via `proposedExecpolicyAmendment` and `proposedNetworkPolicyAmendments`. Clients can prefer `availableDecisions` when present to render the exact set of choices the server wants to expose, while still falling back to the older heuristics if it is omitted.
2. `item/commandExecution/requestApproval` (request) — carries the same `itemId`, `threadId`, `turnId`, the nullable `environmentId` where the command will run, optionally `approvalId` (for subcommand callbacks), and `reason`. New shell and unified-exec approvals set `environmentId`; older events that do not provide one are exposed as `null`. For normal command approvals, the request also includes `command`, `cwd`, and `commandActions` for friendly display. When `initialize.params.capabilities.experimentalApi = true`, it may also include experimental `additionalPermissions` describing requested per-command sandbox access; any filesystem paths in that payload are absolute on the wire, and network access is represented as `additionalPermissions.network.enabled`. For network-only approvals, those command fields may be omitted and `networkApprovalContext` is provided instead. Optional persistence hints may also be included via `proposedExecpolicyAmendment` and `proposedNetworkPolicyAmendments`. Clients can prefer `availableDecisions` when present to render the exact set of choices the server wants to expose, while still falling back to the older heuristics if it is omitted.
3. Client response — for example `{ "decision": "accept" }`, `{ "decision": "acceptForSession" }`, `{ "decision": { "acceptWithExecpolicyAmendment": { "execpolicy_amendment": [...] } } }`, `{ "decision": { "applyNetworkPolicyAmendment": { "network_policy_amendment": { "host": "example.com", "action": "allow" } } } }`, `{ "decision": "decline" }`, or `{ "decision": "cancel" }`.
4. `serverRequest/resolved` — `{ threadId, requestId }` confirms the pending request has been resolved or cleared, including lifecycle cleanup on turn start/complete/interrupt.
5. `item/completed` — final `commandExecution` item with `status: "completed" | "failed" | "declined"` and execution output. Render this as the authoritative result.
@@ -550,6 +550,7 @@ pub(crate) async fn apply_bespoke_event_handling(
call_id,
approval_id,
turn_id,
environment_id,
started_at_ms,
command,
cwd,
@@ -626,6 +627,7 @@ pub(crate) async fn apply_bespoke_event_handling(
item_id: call_id.clone(),
started_at_ms,
approval_id: approval_id.clone(),
environment_id,
reason,
network_approval_context,
command,
@@ -977,6 +977,7 @@ mod tests {
item_id: "item-1".to_string(),
started_at_ms: 0,
approval_id: None,
environment_id: None,
reason: None,
network_approval_context: None,
command: Some("echo hi".to_string()),
@@ -250,6 +250,7 @@ async fn command_execution_request_approval_strips_additional_permissions_withou
item_id: "call_123".to_string(),
started_at_ms: 0,
approval_id: None,
environment_id: None,
reason: Some("Need extra read access".to_string()),
network_approval_context: None,
command: Some("cat file".to_string()),
@@ -315,6 +316,7 @@ async fn command_execution_request_approval_keeps_additional_permissions_with_ca
item_id: "call_123".to_string(),
started_at_ms: 0,
approval_id: None,
environment_id: None,
reason: Some("Need extra read access".to_string()),
network_approval_context: None,
command: Some("cat file".to_string()),
@@ -2067,6 +2067,7 @@ async fn turn_start_exec_approval_toggle_v2() -> Result<()> {
panic!("expected CommandExecutionRequestApproval request");
};
assert_eq!(params.item_id, "call1");
assert_eq!(params.environment_id.as_deref(), Some("local"));
let resolved_request_id = request_id.clone();
// Approve and wait for task completion
+2
View File
@@ -460,6 +460,7 @@ async fn handle_exec_approval(
let ExecApprovalRequestEvent {
call_id,
approval_id,
environment_id,
command,
cwd,
reason,
@@ -505,6 +506,7 @@ async fn handle_exec_approval(
parent_ctx,
call_id,
approval_id,
environment_id,
command,
cwd,
reason,
@@ -322,6 +322,7 @@ async fn handle_exec_approval_uses_call_id_for_guardian_review_and_approval_id_f
call_id: "command-item-1".to_string(),
approval_id: Some("callback-approval-1".to_string()),
turn_id: "child-turn-1".to_string(),
environment_id: Some("remote".to_string()),
started_at_ms: 0,
command: vec!["rm".to_string(), "-rf".to_string(), "tmp".to_string()],
cwd: test_path_buf("/tmp").abs(),
+2
View File
@@ -2039,6 +2039,7 @@ impl Session {
turn_context: &TurnContext,
call_id: String,
approval_id: Option<String>,
environment_id: Option<String>,
command: Vec<String>,
cwd: AbsolutePathBuf,
reason: Option<String>,
@@ -2091,6 +2092,7 @@ impl Session {
call_id,
approval_id,
turn_id: turn_context.sub_id.clone(),
environment_id,
started_at_ms: now_unix_timestamp_ms(),
command,
cwd,
@@ -524,6 +524,7 @@ impl NetworkApprovalService {
turn_context.as_ref(),
guardian_approval_id,
/*approval_id*/ None,
/*environment_id*/ None,
prompt_command,
#[allow(deprecated)]
turn_context.cwd.clone(),
@@ -93,6 +93,7 @@ pub struct ShellRuntime {
#[derive(serde::Serialize, Clone, Debug, Eq, PartialEq, Hash)]
pub(crate) struct ApprovalKey {
environment_id: String,
command: Vec<String>,
cwd: AbsolutePathBuf,
sandbox_permissions: SandboxPermissions,
@@ -127,6 +128,7 @@ impl Approvable<ShellRequest> for ShellRuntime {
fn approval_keys(&self, req: &ShellRequest) -> Vec<Self::ApprovalKey> {
vec![ApprovalKey {
environment_id: req.turn_environment.environment_id.clone(),
command: canonicalize_command_for_approval(&req.command),
cwd: req.cwd.clone(),
sandbox_permissions: req.sandbox_permissions,
@@ -142,6 +144,7 @@ impl Approvable<ShellRequest> for ShellRuntime {
let keys = self.approval_keys(req);
let command = req.command.clone();
let cwd = req.cwd.clone();
let environment_id = Some(req.turn_environment.environment_id.clone());
let retry_reason = ctx.retry_reason.clone();
let reason = retry_reason.clone().or_else(|| req.justification.clone());
let session = ctx.session;
@@ -173,6 +176,7 @@ impl Approvable<ShellRequest> for ShellRuntime {
turn,
call_id,
/*approval_id*/ None,
environment_id,
command,
cwd,
reason,
@@ -325,3 +329,7 @@ impl ToolRuntime<ShellRequest, ExecToolCallOutput> for ShellRuntime {
Ok(out)
}
}
#[cfg(test)]
#[path = "shell_tests.rs"]
mod tests;
@@ -222,6 +222,7 @@ pub(super) async fn try_run_zsh_fork(
session: Arc::clone(&ctx.session),
turn: Arc::clone(&ctx.turn),
call_id: ctx.call_id.clone(),
environment_id: req.turn_environment.environment_id.clone(),
tool_name: GuardianCommandSource::Shell,
approval_policy: ctx.turn.approval_policy.value(),
permission_profile: command_executor.permission_profile.clone(),
@@ -294,6 +295,7 @@ pub(crate) async fn prepare_unified_exec_zsh_fork(
session: Arc::clone(&ctx.session),
turn: Arc::clone(&ctx.turn),
call_id: ctx.call_id.clone(),
environment_id: req.turn_environment.environment_id.clone(),
tool_name: GuardianCommandSource::UnifiedExec,
approval_policy: ctx.turn.approval_policy.value(),
permission_profile: exec_request.permission_profile.clone(),
@@ -328,6 +330,7 @@ struct CoreShellActionProvider {
session: Arc<crate::session::session::Session>,
turn: Arc<crate::session::turn_context::TurnContext>,
call_id: String,
environment_id: String,
tool_name: GuardianCommandSource,
approval_policy: AskForApproval,
permission_profile: PermissionProfile,
@@ -424,6 +427,7 @@ impl CoreShellActionProvider {
let turn = self.turn.clone();
let call_id = self.call_id.clone();
let approval_id = Some(Uuid::new_v4().to_string());
let environment_id = Some(self.environment_id.clone());
let source = self.tool_name;
let guardian_review_id = routes_approval_to_guardian(&turn).then(new_guardian_review_id);
Ok(stopwatch
@@ -489,6 +493,7 @@ impl CoreShellActionProvider {
&turn,
call_id,
approval_id,
environment_id,
command,
workdir.clone(),
/*reason*/ None,
@@ -425,6 +425,7 @@ async fn preapproved_additional_permissions_escalate_intercepted_exec() -> anyho
session: Arc::new(session),
turn: Arc::new(turn_context),
call_id: "preapproved-additional-permissions".to_string(),
environment_id: "local".to_string(),
tool_name: GuardianCommandSource::Shell,
approval_policy: AskForApproval::OnRequest,
permission_profile: permission_profile.clone(),
@@ -560,6 +561,7 @@ async fn execve_permission_request_hook_short_circuits_prompt() -> anyhow::Resul
session: std::sync::Arc::new(session),
turn: std::sync::Arc::new(turn_context),
call_id: "execve-hook-call".to_string(),
environment_id: "local".to_string(),
tool_name: GuardianCommandSource::Shell,
approval_policy: AskForApproval::OnRequest,
permission_profile: PermissionProfile::read_only(),
@@ -770,6 +772,7 @@ prefix_rule(pattern = ["{cat_path_literal}"], decision = "allow")
session: Arc::new(session),
turn: Arc::new(turn_context),
call_id: "deny-read-prefix-allow".to_string(),
environment_id: "local".to_string(),
tool_name: GuardianCommandSource::Shell,
approval_policy: AskForApproval::OnRequest,
permission_profile,
@@ -806,6 +809,7 @@ async fn denied_reads_keep_granular_sandbox_rejection_for_escalation() -> anyhow
session: Arc::new(session),
turn: Arc::new(turn_context),
call_id: "deny-read-granular-sandbox-reject".to_string(),
environment_id: "local".to_string(),
tool_name: GuardianCommandSource::Shell,
approval_policy: AskForApproval::Granular(GranularApprovalConfig {
sandbox_approval: false,
@@ -0,0 +1,42 @@
use super::*;
use codex_exec_server::Environment;
use codex_utils_path_uri::PathUri;
use std::sync::Arc;
#[tokio::test]
async fn approval_key_includes_environment_id() {
let cwd = AbsolutePathBuf::try_from(std::env::current_dir().expect("read current dir"))
.expect("current dir is absolute");
let mut request = ShellRequest {
command: vec!["echo".to_string(), "hello".to_string()],
turn_environment: TurnEnvironment::new(
"remote".to_string(),
Arc::new(Environment::default_for_tests()),
PathUri::from_abs_path(&cwd),
/*shell*/ None,
),
shell_type: None,
hook_command: "echo hello".to_string(),
cwd: cwd.clone(),
timeout_ms: None,
cancellation_token: CancellationToken::new(),
env: HashMap::new(),
explicit_env_overrides: HashMap::new(),
network: None,
sandbox_permissions: SandboxPermissions::UseDefault,
additional_permissions: None,
#[cfg(unix)]
additional_permissions_preapproved: false,
justification: None,
exec_approval_requirement: ExecApprovalRequirement::Skip {
bypass_sandbox: false,
proposed_execpolicy_amendment: None,
},
};
let runtime = ShellRuntime::for_shell_command(ShellRuntimeBackend::ShellCommandClassic);
let original_key = runtime.approval_keys(&request);
request.turn_environment.environment_id = "other".to_string();
let other_key = runtime.approval_keys(&request);
assert_ne!(original_key, other_key);
}
@@ -83,6 +83,7 @@ pub struct UnifiedExecRequest {
/// unified-exec launches.
#[derive(serde::Serialize, Clone, Debug, Eq, PartialEq, Hash)]
pub struct UnifiedExecApprovalKey {
pub environment_id: String,
pub command: Vec<String>,
pub cwd: AbsolutePathBuf,
pub tty: bool,
@@ -135,6 +136,7 @@ impl Approvable<UnifiedExecRequest> for UnifiedExecRuntime<'_> {
fn approval_keys(&self, req: &UnifiedExecRequest) -> Vec<Self::ApprovalKey> {
vec![UnifiedExecApprovalKey {
environment_id: req.turn_environment.environment_id.clone(),
command: canonicalize_command_for_approval(&req.command),
cwd: req.cwd.clone(),
tty: req.tty,
@@ -154,6 +156,7 @@ impl Approvable<UnifiedExecRequest> for UnifiedExecRuntime<'_> {
let call_id = ctx.call_id.to_string();
let command = req.command.clone();
let cwd = req.cwd.clone();
let environment_id = Some(req.turn_environment.environment_id.clone());
let retry_reason = ctx.retry_reason.clone();
let reason = retry_reason.clone().or_else(|| req.justification.clone());
let guardian_review_id = ctx.guardian_review_id.clone();
@@ -183,6 +186,7 @@ impl Approvable<UnifiedExecRequest> for UnifiedExecRuntime<'_> {
turn,
call_id,
/*approval_id*/ None,
environment_id,
command,
cwd.clone(),
reason,
@@ -463,6 +467,25 @@ mod tests {
}
}
#[tokio::test]
async fn approval_key_includes_environment_id() {
let manager = UnifiedExecProcessManager::default();
let runtime = UnifiedExecRuntime::new(&manager, UnifiedExecShellMode::Direct);
let mut request = test_request(
SandboxPermissions::UseDefault,
ExecApprovalRequirement::Skip {
bypass_sandbox: false,
proposed_execpolicy_amendment: None,
},
);
request.turn_environment.environment_id = "remote".to_string();
let original_key = runtime.approval_keys(&request);
request.turn_environment.environment_id = "other".to_string();
let other_key = runtime.approval_keys(&request);
assert_ne!(original_key, other_key);
}
#[tokio::test]
async fn unified_exec_uses_the_trusted_sandbox_cwd() {
let cwd_dir = tempdir().expect("create process temp dir");
@@ -220,6 +220,7 @@ async fn run_codex_tool_session_inner(
let approval_id = ev.effective_approval_id();
let ExecApprovalRequestEvent {
turn_id: _,
environment_id: _,
started_at_ms: _,
command,
cwd,
+10
View File
@@ -229,6 +229,16 @@ pub struct ExecApprovalRequestEvent {
/// Uses `#[serde(default)]` for backwards compatibility.
#[serde(default)]
pub turn_id: String,
/// Environment in which the command will run.
#[serde(
default,
rename = "environmentId",
alias = "environment_id",
skip_serializing_if = "Option::is_none"
)]
#[ts(optional)]
#[ts(rename = "environmentId")]
pub environment_id: Option<String>,
#[ts(type = "number")]
pub started_at_ms: i64,
/// The command to be executed.
@@ -452,6 +452,7 @@ mod tests {
item_id: "call-1".to_string(),
started_at_ms: 0,
approval_id: Some("approval-1".to_string()),
environment_id: None,
reason: None,
network_approval_context: None,
command: Some("ls".to_string()),
@@ -791,6 +792,7 @@ mod tests {
item_id: "call-1".to_string(),
started_at_ms: 0,
approval_id: Some("approval-1".to_string()),
environment_id: None,
reason: None,
network_approval_context: None,
command: Some("ls".to_string()),
@@ -615,6 +615,7 @@ mod tests {
item_id: call_id.to_string(),
started_at_ms: 0,
approval_id: approval_id.map(str::to_string),
environment_id: None,
reason: None,
network_approval_context: None,
command: Some("echo hi".to_string()),
+1
View File
@@ -4690,6 +4690,7 @@ fn exec_approval_request(
item_id: item_id.to_string(),
started_at_ms: 0,
approval_id: approval_id.map(str::to_string),
environment_id: None,
reason: Some("needs approval".to_string()),
network_approval_context: None,
command: Some("echo hello".to_string()),
+1
View File
@@ -488,6 +488,7 @@ mod tests {
item_id: item_id.to_string(),
started_at_ms: 0,
approval_id: approval_id.map(str::to_string),
environment_id: None,
reason: Some("needs approval".to_string()),
network_approval_context: None,
command: Some("echo hello".to_string()),
+1
View File
@@ -226,6 +226,7 @@ impl App {
.approval_id
.clone()
.unwrap_or_else(|| params.item_id.clone()),
environment_id: params.environment_id.clone(),
command: params
.command
.as_deref()
+2
View File
@@ -26,6 +26,8 @@ pub(crate) struct ExecApprovalRequestEvent {
pub(crate) approval_id: Option<String>,
#[serde(default)]
pub(crate) turn_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) environment_id: Option<String>,
pub(crate) command: Vec<String>,
pub(crate) cwd: AbsolutePathBuf,
pub(crate) reason: Option<String>,
@@ -74,6 +74,7 @@ pub(crate) enum ApprovalRequest {
thread_id: ThreadId,
thread_label: Option<String>,
id: String,
environment_id: Option<String>,
command: Vec<String>,
reason: Option<String>,
available_decisions: Vec<CommandExecutionApprovalDecision>,
@@ -675,6 +676,7 @@ fn build_header(request: &ApprovalRequest) -> Box<dyn Renderable> {
match request {
ApprovalRequest::Exec {
thread_label,
environment_id,
reason,
command,
network_approval_context,
@@ -689,6 +691,13 @@ fn build_header(request: &ApprovalRequest) -> Box<dyn Renderable> {
]));
header.push(Line::from(""));
}
if let Some(environment_id) = environment_id {
header.push(Line::from(vec![
"Environment: ".into(),
environment_id.clone().bold(),
]));
header.push(Line::from(""));
}
if let Some(reason) = reason {
header.push(Line::from(vec!["Reason: ".into(), reason.clone().italic()]));
header.push(Line::from(""));
@@ -1220,6 +1229,7 @@ mod tests {
thread_id: ThreadId::new(),
thread_label: None,
id: "test".to_string(),
environment_id: None,
command: vec!["echo".to_string(), "hi".to_string()],
reason: Some("reason".to_string()),
available_decisions: vec![
@@ -1360,6 +1370,7 @@ mod tests {
thread_id: ThreadId::new(),
thread_label: None,
id: "test".to_string(),
environment_id: None,
command: vec!["echo".to_string(), "hi".to_string()],
reason: None,
available_decisions: vec![
@@ -1403,6 +1414,7 @@ mod tests {
thread_id: ThreadId::new(),
thread_label: None,
id: "test".to_string(),
environment_id: None,
command: vec!["curl".to_string(), "https://example.com".to_string()],
reason: None,
available_decisions: vec![
@@ -1477,6 +1489,7 @@ mod tests {
thread_id,
thread_label: Some("Robie [explorer]".to_string()),
id: "test".to_string(),
environment_id: None,
command: vec!["echo".to_string(), "hi".to_string()],
reason: None,
available_decisions: vec![
@@ -1511,6 +1524,7 @@ mod tests {
thread_id,
thread_label: Some("Robie [explorer]".to_string()),
id: "test".to_string(),
environment_id: None,
command: vec!["echo".to_string(), "hi".to_string()],
reason: None,
available_decisions: vec![
@@ -1549,6 +1563,7 @@ mod tests {
thread_id: ThreadId::new(),
thread_label: Some("Robie [explorer]".to_string()),
id: "test".to_string(),
environment_id: None,
command: vec!["echo".to_string(), "hi".to_string()],
reason: None,
available_decisions: vec![
@@ -1577,6 +1592,7 @@ mod tests {
thread_id: ThreadId::new(),
thread_label: None,
id: "test".to_string(),
environment_id: None,
command: vec!["echo".to_string()],
reason: None,
available_decisions: vec![
@@ -1629,6 +1645,7 @@ mod tests {
thread_id: ThreadId::new(),
thread_label: None,
id: "test".to_string(),
environment_id: None,
command: vec!["curl".to_string(), "https://example.com".to_string()],
reason: None,
available_decisions: vec![
@@ -1668,6 +1685,7 @@ mod tests {
thread_id: ThreadId::new(),
thread_label: None,
id: "test".into(),
environment_id: None,
command,
reason: None,
available_decisions: vec![
@@ -1966,6 +1984,7 @@ mod tests {
thread_id: ThreadId::new(),
thread_label: None,
id: "test".into(),
environment_id: None,
command: vec!["cat".into(), "/tmp/readme.txt".into()],
reason: None,
available_decisions: vec![
@@ -2022,6 +2041,7 @@ mod tests {
thread_id: ThreadId::new(),
thread_label: None,
id: "test".into(),
environment_id: None,
command: vec!["cat".into(), "/tmp/readme.txt".into()],
reason: Some("need filesystem access".into()),
available_decisions: vec![
@@ -2102,6 +2122,7 @@ mod tests {
thread_id: ThreadId::new(),
thread_label: None,
id: "test".into(),
environment_id: None,
command: vec!["curl".into(), "https://example.com".into()],
reason: Some("network request blocked".into()),
available_decisions: vec![
@@ -2238,6 +2259,7 @@ mod tests {
thread_id: ThreadId::new(),
thread_label: None,
id: "test".into(),
environment_id: None,
command: vec![
"network-access".to_string(),
"https://example.com:8443".to_string(),
+1
View File
@@ -1896,6 +1896,7 @@ mod tests {
thread_id: codex_protocol::ThreadId::new(),
thread_label: None,
id: "1".to_string(),
environment_id: None,
command: vec!["echo".into(), "ok".into()],
reason: None,
available_decisions: vec![
+1
View File
@@ -852,6 +852,7 @@ fn exec_approval_request_from_params(
additional_permissions: params.additional_permissions,
turn_id: params.turn_id,
approval_id: params.approval_id,
environment_id: params.environment_id,
proposed_execpolicy_amendment: params.proposed_execpolicy_amendment,
proposed_network_policy_amendments: params.proposed_network_policy_amendments,
available_decisions: params.available_decisions,
@@ -161,6 +161,7 @@ mod tests {
call_id: call_id.to_string(),
approval_id: approval_id.map(str::to_string),
turn_id: "turn".to_string(),
environment_id: None,
command: vec!["true".to_string()],
cwd: AbsolutePathBuf::current_dir().expect("current dir"),
reason: None,
@@ -13,6 +13,7 @@ async fn exec_approval_emits_proposed_command_and_decision_history() {
call_id: "call-short".into(),
approval_id: Some("call-short".into()),
turn_id: "turn-short".into(),
environment_id: Some("remote".to_string()),
command: vec!["bash".into(), "-lc".into(), "echo hello world".into()],
cwd: AbsolutePathBuf::current_dir().expect("current dir"),
reason: Some(
@@ -57,6 +58,7 @@ fn app_server_exec_approval_request_splits_shell_wrapped_command() {
item_id: "item-1".to_string(),
started_at_ms: 0,
approval_id: Some("approval-1".to_string()),
environment_id: None,
reason: None,
network_approval_context: None,
command: Some(
@@ -98,6 +100,7 @@ fn app_server_exec_approval_request_preserves_permissions_context() {
item_id: "item-1".to_string(),
started_at_ms: 0,
approval_id: Some("approval-1".to_string()),
environment_id: None,
reason: None,
network_approval_context: Some(codex_app_server_protocol::NetworkApprovalContext {
host: "example.com".to_string(),
@@ -157,6 +160,7 @@ async fn network_exec_approval_history_describes_session_host_allowance() {
item_id: "item-1".to_string(),
started_at_ms: 0,
approval_id: Some("approval-1".to_string()),
environment_id: None,
reason: None,
network_approval_context: Some(codex_app_server_protocol::NetworkApprovalContext {
host: "example.com".to_string(),
@@ -198,6 +202,7 @@ async fn network_exec_approval_history_describes_one_time_host_allowance() {
item_id: "item-1".to_string(),
started_at_ms: 0,
approval_id: Some("approval-1".to_string()),
environment_id: None,
reason: None,
network_approval_context: Some(codex_app_server_protocol::NetworkApprovalContext {
host: "example.com".to_string(),
@@ -239,6 +244,7 @@ async fn network_exec_approval_history_describes_canceled_host_request() {
item_id: "item-1".to_string(),
started_at_ms: 0,
approval_id: Some("approval-1".to_string()),
environment_id: None,
reason: None,
network_approval_context: Some(codex_app_server_protocol::NetworkApprovalContext {
host: "example.com".to_string(),
@@ -330,6 +336,7 @@ async fn exec_approval_uses_approval_id_when_present() {
call_id: "call-parent".into(),
approval_id: Some("approval-subcommand".into()),
turn_id: "turn-short".into(),
environment_id: None,
command: vec!["bash".into(), "-lc".into(), "echo hello world".into()],
cwd: AbsolutePathBuf::current_dir().expect("current dir"),
reason: Some(
@@ -372,6 +379,7 @@ async fn exec_approval_decision_truncates_multiline_and_long_commands() {
call_id: "call-multi".into(),
approval_id: Some("call-multi".into()),
turn_id: "turn-multi".into(),
environment_id: None,
command: vec!["bash".into(), "-lc".into(), "echo line1\necho line2".into()],
cwd: AbsolutePathBuf::current_dir().expect("current dir"),
reason: Some(
@@ -423,6 +431,7 @@ async fn exec_approval_decision_truncates_multiline_and_long_commands() {
call_id: "call-long".into(),
approval_id: Some("call-long".into()),
turn_id: "turn-long".into(),
environment_id: None,
command: vec!["bash".into(), "-lc".into(), long],
cwd: AbsolutePathBuf::current_dir().expect("current dir"),
reason: None,
@@ -10,6 +10,7 @@ async fn exec_approval_emits_proposed_command_and_decision_history() {
call_id: "call-short".into(),
approval_id: Some("call-short".into()),
turn_id: "turn-short".into(),
environment_id: None,
command: vec!["bash".into(), "-lc".into(), "echo hello world".into()],
cwd: AbsolutePathBuf::current_dir().expect("current dir"),
reason: Some(
@@ -56,6 +57,7 @@ fn app_server_exec_approval_request_splits_shell_wrapped_command() {
item_id: "item-1".to_string(),
started_at_ms: 0,
approval_id: Some("approval-1".to_string()),
environment_id: None,
reason: None,
network_approval_context: None,
command: Some(
@@ -93,6 +95,7 @@ async fn exec_approval_uses_approval_id_when_present() {
call_id: "call-parent".into(),
approval_id: Some("approval-subcommand".into()),
turn_id: "turn-short".into(),
environment_id: None,
command: vec!["bash".into(), "-lc".into(), "echo hello world".into()],
cwd: AbsolutePathBuf::current_dir().expect("current dir"),
reason: Some(
@@ -136,6 +139,7 @@ async fn exec_approval_decision_truncates_multiline_and_long_commands() {
call_id: "call-multi".into(),
approval_id: Some("call-multi".into()),
turn_id: "turn-multi".into(),
environment_id: None,
command: vec!["bash".into(), "-lc".into(), "echo line1\necho line2".into()],
cwd: AbsolutePathBuf::current_dir().expect("current dir"),
reason: Some(
@@ -189,6 +193,7 @@ async fn exec_approval_decision_truncates_multiline_and_long_commands() {
call_id: "call-long".into(),
approval_id: Some("call-long".into()),
turn_id: "turn-long".into(),
environment_id: None,
command: vec!["bash".into(), "-lc".into(), long],
cwd: AbsolutePathBuf::current_dir().expect("current dir"),
reason: None,
@@ -1098,6 +1103,7 @@ async fn approval_modal_exec_snapshot() -> anyhow::Result<()> {
call_id: "call-approve-cmd".into(),
approval_id: Some("call-approve-cmd".into()),
turn_id: "turn-approve-cmd".into(),
environment_id: None,
command: vec!["bash".into(), "-lc".into(), "echo hello world".into()],
cwd: AbsolutePathBuf::current_dir().expect("current dir"),
reason: Some(
@@ -1155,6 +1161,7 @@ async fn approval_modal_exec_without_reason_snapshot() -> anyhow::Result<()> {
call_id: "call-approve-cmd-noreason".into(),
approval_id: Some("call-approve-cmd-noreason".into()),
turn_id: "turn-approve-cmd-noreason".into(),
environment_id: None,
command: vec!["bash".into(), "-lc".into(), "echo hello world".into()],
cwd: AbsolutePathBuf::current_dir().expect("current dir"),
reason: None,
@@ -1201,6 +1208,7 @@ async fn approval_modal_exec_multiline_prefix_hides_execpolicy_option_snapshot()
call_id: "call-approve-cmd-multiline-trunc".into(),
approval_id: Some("call-approve-cmd-multiline-trunc".into()),
turn_id: "turn-approve-cmd-multiline-trunc".into(),
environment_id: None,
command: command.clone(),
cwd: AbsolutePathBuf::current_dir().expect("current dir"),
reason: None,
@@ -3,12 +3,14 @@ source: tui/src/chatwidget/tests/approval_requests.rs
expression: "format!(\"{buf:?}\")"
---
Buffer {
area: Rect { x: 0, y: 0, width: 80, height: 13 },
area: Rect { x: 0, y: 0, width: 80, height: 15 },
content: [
" ",
" ",
" Would you like to run the following command? ",
" ",
" Environment: remote ",
" ",
" Reason: this is a test reason such as one that would be produced by the ",
" model ",
" ",
@@ -23,17 +25,19 @@ Buffer {
x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
x: 2, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: BOLD,
x: 46, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
x: 10, y: 4, fg: Reset, bg: Reset, underline: Reset, modifier: ITALIC,
x: 73, y: 4, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
x: 2, y: 5, fg: Reset, bg: Reset, underline: Reset, modifier: ITALIC,
x: 7, y: 5, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
x: 4, y: 7, fg: Rgb(137, 180, 250), bg: Reset, underline: Reset, modifier: NONE,
x: 8, y: 7, fg: Rgb(205, 214, 244), bg: Reset, underline: Reset, modifier: NONE,
x: 20, y: 7, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
x: 0, y: 9, fg: Cyan, bg: Reset, underline: Reset, modifier: BOLD,
x: 21, y: 9, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
x: 48, y: 10, fg: Reset, bg: Reset, underline: Reset, modifier: DIM,
x: 51, y: 10, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
x: 2, y: 12, fg: Reset, bg: Reset, underline: Reset, modifier: DIM,
x: 15, y: 4, fg: Reset, bg: Reset, underline: Reset, modifier: BOLD,
x: 21, y: 4, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
x: 10, y: 6, fg: Reset, bg: Reset, underline: Reset, modifier: ITALIC,
x: 73, y: 6, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
x: 2, y: 7, fg: Reset, bg: Reset, underline: Reset, modifier: ITALIC,
x: 7, y: 7, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
x: 4, y: 9, fg: Rgb(137, 180, 250), bg: Reset, underline: Reset, modifier: NONE,
x: 8, y: 9, fg: Rgb(205, 214, 244), bg: Reset, underline: Reset, modifier: NONE,
x: 20, y: 9, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
x: 0, y: 11, fg: Cyan, bg: Reset, underline: Reset, modifier: BOLD,
x: 21, y: 11, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
x: 48, y: 12, fg: Reset, bg: Reset, underline: Reset, modifier: DIM,
x: 51, y: 12, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
x: 2, y: 14, fg: Reset, bg: Reset, underline: Reset, modifier: DIM,
]
}
@@ -1864,6 +1864,7 @@ async fn status_widget_and_approval_modal_snapshot() {
call_id: "call-approve-exec".into(),
approval_id: Some("call-approve-exec".into()),
turn_id: "turn-approve-exec".into(),
environment_id: None,
command: vec!["echo".into(), "hello world".into()],
cwd: test_path_buf("/tmp").abs(),
reason: Some(
@@ -13,6 +13,7 @@ async fn terminal_title_shows_action_required_while_exec_approval_is_pending() {
call_id: "call-action-required".into(),
approval_id: Some("call-action-required".into()),
turn_id: "turn-action-required".into(),
environment_id: None,
command: vec!["bash".into(), "-lc".into(), "echo hello".into()],
cwd: AbsolutePathBuf::current_dir().expect("current dir"),
reason: Some("need confirmation".into()),
@@ -55,6 +56,7 @@ async fn terminal_title_action_required_respects_spinner_setting() {
call_id: "call-no-spinner".into(),
approval_id: Some("call-no-spinner".into()),
turn_id: "turn-no-spinner".into(),
environment_id: None,
command: vec!["bash".into(), "-lc".into(), "echo hello".into()],
cwd: AbsolutePathBuf::current_dir().expect("current dir"),
reason: Some("need confirmation".into()),
@@ -83,6 +85,7 @@ async fn terminal_title_action_required_blinks_when_animations_are_enabled() {
call_id: "call-blink".into(),
approval_id: Some("call-blink".into()),
turn_id: "turn-blink".into(),
environment_id: None,
command: vec!["bash".into(), "-lc".into(), "echo hello".into()],
cwd: AbsolutePathBuf::current_dir().expect("current dir"),
reason: Some("need confirmation".into()),
@@ -118,6 +121,7 @@ async fn terminal_title_activity_indicators_do_not_animate_when_animations_are_d
call_id: "call-no-animations".into(),
approval_id: Some("call-no-animations".into()),
turn_id: "turn-no-animations".into(),
environment_id: None,
command: vec!["bash".into(), "-lc".into(), "echo hello".into()],
cwd: AbsolutePathBuf::current_dir().expect("current dir"),
reason: Some("need confirmation".into()),
@@ -296,6 +296,7 @@ impl ChatWidget {
thread_id: self.thread_id.unwrap_or_default(),
thread_label: None,
id: ev.effective_approval_id(),
environment_id: ev.environment_id,
command: ev.command,
reason: ev.reason,
available_decisions,