mirror of
https://github.com/openai/codex.git
synced 2026-11-13 21:30:47 +00:00
protocol: retain executor cwd in command events
This commit is contained in:
@@ -6,6 +6,8 @@ use codex_async_utils::CancelErr;
|
||||
use codex_async_utils::OrCancelExt;
|
||||
use codex_network_proxy::PROXY_ACTIVE_ENV_KEY;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::PathConvention;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::error;
|
||||
use uuid::Uuid;
|
||||
@@ -150,6 +152,7 @@ pub(crate) async fn execute_user_shell_command(
|
||||
let raw_command = command;
|
||||
#[allow(deprecated)]
|
||||
let cwd = turn_context.cwd.clone();
|
||||
let cwd_uri = PathUri::from_abs_path(&cwd);
|
||||
|
||||
let parsed_cmd = parse_command(&display_command);
|
||||
session
|
||||
@@ -161,7 +164,8 @@ pub(crate) async fn execute_user_shell_command(
|
||||
turn_id: turn_context.sub_id.clone(),
|
||||
started_at_ms: now_unix_timestamp_ms(),
|
||||
command: display_command.clone(),
|
||||
cwd: cwd.clone(),
|
||||
cwd: cwd_uri.clone(),
|
||||
path_convention: PathConvention::native(),
|
||||
parsed_cmd: parsed_cmd.clone(),
|
||||
source: ExecCommandSource::UserShell,
|
||||
interaction_input: None,
|
||||
@@ -235,7 +239,8 @@ pub(crate) async fn execute_user_shell_command(
|
||||
turn_id: turn_context.sub_id.clone(),
|
||||
completed_at_ms: now_unix_timestamp_ms(),
|
||||
command: display_command.clone(),
|
||||
cwd: cwd.clone(),
|
||||
cwd: cwd_uri.clone(),
|
||||
path_convention: PathConvention::native(),
|
||||
parsed_cmd: parsed_cmd.clone(),
|
||||
source: ExecCommandSource::UserShell,
|
||||
interaction_input: None,
|
||||
@@ -260,7 +265,8 @@ pub(crate) async fn execute_user_shell_command(
|
||||
turn_id: turn_context.sub_id.clone(),
|
||||
completed_at_ms: now_unix_timestamp_ms(),
|
||||
command: display_command.clone(),
|
||||
cwd: cwd.clone(),
|
||||
cwd: cwd_uri.clone(),
|
||||
path_convention: PathConvention::native(),
|
||||
parsed_cmd: parsed_cmd.clone(),
|
||||
source: ExecCommandSource::UserShell,
|
||||
interaction_input: None,
|
||||
@@ -305,7 +311,8 @@ pub(crate) async fn execute_user_shell_command(
|
||||
turn_id: turn_context.sub_id.clone(),
|
||||
completed_at_ms: now_unix_timestamp_ms(),
|
||||
command: display_command,
|
||||
cwd,
|
||||
cwd: cwd_uri,
|
||||
path_convention: PathConvention::native(),
|
||||
parsed_cmd,
|
||||
source: ExecCommandSource::UserShell,
|
||||
interaction_input: None,
|
||||
|
||||
@@ -21,6 +21,8 @@ use codex_protocol::protocol::PatchApplyStatus;
|
||||
use codex_protocol::protocol::TurnDiffEvent;
|
||||
use codex_shell_command::parse_command::parse_command;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::PathConvention;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
@@ -92,28 +94,21 @@ fn tracker_update_for_known_delta<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn emit_exec_command_begin(
|
||||
ctx: ToolEventCtx<'_>,
|
||||
command: &[String],
|
||||
cwd: &AbsolutePathBuf,
|
||||
parsed_cmd: &[ParsedCommand],
|
||||
source: ExecCommandSource,
|
||||
interaction_input: Option<String>,
|
||||
process_id: Option<&str>,
|
||||
) {
|
||||
async fn emit_exec_command_begin(ctx: ToolEventCtx<'_>, exec_input: ExecCommandInput<'_>) {
|
||||
ctx.session
|
||||
.send_event(
|
||||
ctx.turn,
|
||||
EventMsg::ExecCommandBegin(ExecCommandBeginEvent {
|
||||
call_id: ctx.call_id.to_string(),
|
||||
process_id: process_id.map(str::to_owned),
|
||||
process_id: exec_input.process_id.map(str::to_owned),
|
||||
turn_id: ctx.turn.sub_id.clone(),
|
||||
started_at_ms: now_unix_timestamp_ms(),
|
||||
command: command.to_vec(),
|
||||
cwd: cwd.clone(),
|
||||
parsed_cmd: parsed_cmd.to_vec(),
|
||||
source,
|
||||
interaction_input,
|
||||
command: exec_input.command.to_vec(),
|
||||
cwd: exec_input.cwd.clone(),
|
||||
path_convention: exec_input.path_convention,
|
||||
parsed_cmd: exec_input.parsed_cmd.to_vec(),
|
||||
source: exec_input.source,
|
||||
interaction_input: exec_input.interaction_input.map(str::to_owned),
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
@@ -122,7 +117,8 @@ pub(crate) async fn emit_exec_command_begin(
|
||||
pub(crate) enum ToolEmitter {
|
||||
Shell {
|
||||
command: Vec<String>,
|
||||
cwd: AbsolutePathBuf,
|
||||
cwd: PathUri,
|
||||
path_convention: PathConvention,
|
||||
source: ExecCommandSource,
|
||||
parsed_cmd: Vec<ParsedCommand>,
|
||||
},
|
||||
@@ -133,7 +129,8 @@ pub(crate) enum ToolEmitter {
|
||||
},
|
||||
UnifiedExec {
|
||||
command: Vec<String>,
|
||||
cwd: AbsolutePathBuf,
|
||||
cwd: PathUri,
|
||||
path_convention: PathConvention,
|
||||
source: ExecCommandSource,
|
||||
parsed_cmd: Vec<ParsedCommand>,
|
||||
process_id: Option<String>,
|
||||
@@ -145,7 +142,8 @@ impl ToolEmitter {
|
||||
let parsed_cmd = parse_command(&command);
|
||||
Self::Shell {
|
||||
command,
|
||||
cwd,
|
||||
cwd: PathUri::from_abs_path(&cwd),
|
||||
path_convention: PathConvention::native(),
|
||||
source,
|
||||
parsed_cmd,
|
||||
}
|
||||
@@ -165,7 +163,8 @@ impl ToolEmitter {
|
||||
|
||||
pub fn unified_exec(
|
||||
command: &[String],
|
||||
cwd: AbsolutePathBuf,
|
||||
cwd: PathUri,
|
||||
path_convention: PathConvention,
|
||||
source: ExecCommandSource,
|
||||
process_id: Option<String>,
|
||||
) -> Self {
|
||||
@@ -173,6 +172,7 @@ impl ToolEmitter {
|
||||
Self::UnifiedExec {
|
||||
command: command.to_vec(),
|
||||
cwd,
|
||||
path_convention,
|
||||
source,
|
||||
parsed_cmd,
|
||||
process_id,
|
||||
@@ -185,6 +185,7 @@ impl ToolEmitter {
|
||||
Self::Shell {
|
||||
command,
|
||||
cwd,
|
||||
path_convention,
|
||||
source,
|
||||
parsed_cmd,
|
||||
..
|
||||
@@ -194,7 +195,12 @@ impl ToolEmitter {
|
||||
emit_exec_stage(
|
||||
ctx,
|
||||
ExecCommandInput::new(
|
||||
command, cwd, parsed_cmd, *source, /*interaction_input*/ None,
|
||||
command,
|
||||
cwd,
|
||||
*path_convention,
|
||||
parsed_cmd,
|
||||
*source,
|
||||
/*interaction_input*/ None,
|
||||
/*process_id*/ None,
|
||||
),
|
||||
stage,
|
||||
@@ -314,6 +320,7 @@ impl ToolEmitter {
|
||||
Self::UnifiedExec {
|
||||
command,
|
||||
cwd,
|
||||
path_convention,
|
||||
source,
|
||||
parsed_cmd,
|
||||
process_id,
|
||||
@@ -325,6 +332,7 @@ impl ToolEmitter {
|
||||
ExecCommandInput::new(
|
||||
command,
|
||||
cwd,
|
||||
*path_convention,
|
||||
parsed_cmd,
|
||||
*source,
|
||||
/*interaction_input*/ None,
|
||||
@@ -432,7 +440,8 @@ impl ToolEmitter {
|
||||
|
||||
struct ExecCommandInput<'a> {
|
||||
command: &'a [String],
|
||||
cwd: &'a AbsolutePathBuf,
|
||||
cwd: &'a PathUri,
|
||||
path_convention: PathConvention,
|
||||
parsed_cmd: &'a [ParsedCommand],
|
||||
source: ExecCommandSource,
|
||||
interaction_input: Option<&'a str>,
|
||||
@@ -442,7 +451,8 @@ struct ExecCommandInput<'a> {
|
||||
impl<'a> ExecCommandInput<'a> {
|
||||
fn new(
|
||||
command: &'a [String],
|
||||
cwd: &'a AbsolutePathBuf,
|
||||
cwd: &'a PathUri,
|
||||
path_convention: PathConvention,
|
||||
parsed_cmd: &'a [ParsedCommand],
|
||||
source: ExecCommandSource,
|
||||
interaction_input: Option<&'a str>,
|
||||
@@ -451,6 +461,7 @@ impl<'a> ExecCommandInput<'a> {
|
||||
Self {
|
||||
command,
|
||||
cwd,
|
||||
path_convention,
|
||||
parsed_cmd,
|
||||
source,
|
||||
interaction_input,
|
||||
@@ -476,16 +487,7 @@ async fn emit_exec_stage(
|
||||
) {
|
||||
match stage {
|
||||
ToolEventStage::Begin => {
|
||||
emit_exec_command_begin(
|
||||
ctx,
|
||||
exec_input.command,
|
||||
exec_input.cwd,
|
||||
exec_input.parsed_cmd,
|
||||
exec_input.source,
|
||||
exec_input.interaction_input.map(str::to_owned),
|
||||
exec_input.process_id,
|
||||
)
|
||||
.await;
|
||||
emit_exec_command_begin(ctx, exec_input).await;
|
||||
}
|
||||
ToolEventStage::Success { output, .. }
|
||||
| ToolEventStage::Failure(ToolEventFailure::Output(output)) => {
|
||||
@@ -548,6 +550,7 @@ async fn emit_exec_end(
|
||||
completed_at_ms: now_unix_timestamp_ms(),
|
||||
command: exec_input.command.to_vec(),
|
||||
cwd: exec_input.cwd.clone(),
|
||||
path_convention: exec_input.path_convention,
|
||||
parsed_cmd: exec_input.parsed_cmd.to_vec(),
|
||||
source: exec_input.source,
|
||||
interaction_input: exec_input.interaction_input.map(str::to_owned),
|
||||
|
||||
@@ -237,6 +237,7 @@ impl ExecCommandHandler {
|
||||
max_output_tokens,
|
||||
cwd,
|
||||
cwd_uri,
|
||||
path_convention: turn_environment.path_convention(),
|
||||
sandbox_cwd,
|
||||
environment,
|
||||
shell_mode,
|
||||
@@ -350,6 +351,7 @@ impl ExecCommandHandler {
|
||||
max_output_tokens,
|
||||
cwd,
|
||||
cwd_uri,
|
||||
path_convention: turn_environment.path_convention(),
|
||||
sandbox_cwd,
|
||||
environment,
|
||||
shell_mode,
|
||||
|
||||
@@ -22,7 +22,8 @@ use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::protocol::ExecCommandOutputDeltaEvent;
|
||||
use codex_protocol::protocol::ExecCommandSource;
|
||||
use codex_protocol::protocol::ExecOutputStream;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::PathConvention;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
|
||||
pub(crate) const TRAILING_OUTPUT_GRACE: Duration = Duration::from_millis(100);
|
||||
|
||||
@@ -110,7 +111,8 @@ pub(crate) fn spawn_exit_watcher(
|
||||
turn_ref: Arc<TurnContext>,
|
||||
call_id: String,
|
||||
command: Vec<String>,
|
||||
cwd: AbsolutePathBuf,
|
||||
cwd: PathUri,
|
||||
path_convention: PathConvention,
|
||||
process_id: i32,
|
||||
transcript: Arc<Mutex<HeadTailBuffer>>,
|
||||
started_at: Instant,
|
||||
@@ -130,6 +132,7 @@ pub(crate) fn spawn_exit_watcher(
|
||||
call_id,
|
||||
command,
|
||||
cwd,
|
||||
path_convention,
|
||||
Some(process_id.to_string()),
|
||||
transcript,
|
||||
String::new(),
|
||||
@@ -145,6 +148,7 @@ pub(crate) fn spawn_exit_watcher(
|
||||
call_id,
|
||||
command,
|
||||
cwd,
|
||||
path_convention,
|
||||
Some(process_id.to_string()),
|
||||
transcript,
|
||||
String::new(),
|
||||
@@ -197,7 +201,8 @@ pub(crate) async fn emit_exec_end_for_unified_exec(
|
||||
turn_ref: Arc<TurnContext>,
|
||||
call_id: String,
|
||||
command: Vec<String>,
|
||||
cwd: AbsolutePathBuf,
|
||||
cwd: PathUri,
|
||||
path_convention: PathConvention,
|
||||
process_id: Option<String>,
|
||||
transcript: Arc<Mutex<HeadTailBuffer>>,
|
||||
fallback_output: String,
|
||||
@@ -222,6 +227,7 @@ pub(crate) async fn emit_exec_end_for_unified_exec(
|
||||
let emitter = ToolEmitter::unified_exec(
|
||||
&command,
|
||||
cwd,
|
||||
path_convention,
|
||||
ExecCommandSource::UnifiedExecStartup,
|
||||
process_id,
|
||||
);
|
||||
@@ -242,7 +248,8 @@ pub(crate) async fn emit_failed_exec_end_for_unified_exec(
|
||||
turn_ref: Arc<TurnContext>,
|
||||
call_id: String,
|
||||
command: Vec<String>,
|
||||
cwd: AbsolutePathBuf,
|
||||
cwd: PathUri,
|
||||
path_convention: PathConvention,
|
||||
process_id: Option<String>,
|
||||
transcript: Arc<Mutex<HeadTailBuffer>>,
|
||||
fallback_output: String,
|
||||
@@ -276,6 +283,7 @@ pub(crate) async fn emit_failed_exec_end_for_unified_exec(
|
||||
let emitter = ToolEmitter::unified_exec(
|
||||
&command,
|
||||
cwd,
|
||||
path_convention,
|
||||
ExecCommandSource::UnifiedExecStartup,
|
||||
process_id,
|
||||
);
|
||||
|
||||
@@ -33,6 +33,7 @@ use codex_protocol::models::AdditionalPermissionProfile;
|
||||
use codex_tools::UnifiedExecShellMode;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_output_truncation::TruncationPolicy;
|
||||
use codex_utils_path_uri::PathConvention;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use rand::Rng;
|
||||
use rand::rng;
|
||||
@@ -97,9 +98,10 @@ pub(crate) struct ExecCommandRequest {
|
||||
pub environment_id: String,
|
||||
pub yield_time_ms: u64,
|
||||
pub max_output_tokens: Option<usize>,
|
||||
/// App-host-compatible cwd retained for approval and event compatibility.
|
||||
/// App-host-compatible cwd retained for local sandbox and approval handling.
|
||||
pub cwd: AbsolutePathBuf,
|
||||
pub cwd_uri: PathUri,
|
||||
pub path_convention: PathConvention,
|
||||
pub sandbox_cwd: Option<AbsolutePathBuf>,
|
||||
pub environment: Arc<Environment>,
|
||||
pub shell_mode: UnifiedExecShellMode,
|
||||
|
||||
@@ -58,6 +58,8 @@ use codex_protocol::protocol::ExecCommandSource;
|
||||
use codex_tools::ToolName;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_output_truncation::approx_token_count;
|
||||
use codex_utils_path_uri::PathConvention;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
|
||||
const UNIFIED_EXEC_ENV: [(&str, &str); 10] = [
|
||||
("NO_COLOR", "1"),
|
||||
@@ -337,7 +339,6 @@ async fn emit_failed_initial_exec_end_if_unstored(
|
||||
process_started_alive: bool,
|
||||
context: &UnifiedExecContext,
|
||||
request: &ExecCommandRequest,
|
||||
cwd: AbsolutePathBuf,
|
||||
transcript: Arc<tokio::sync::Mutex<HeadTailBuffer>>,
|
||||
fallback_output: String,
|
||||
message: String,
|
||||
@@ -352,7 +353,8 @@ async fn emit_failed_initial_exec_end_if_unstored(
|
||||
Arc::clone(&context.turn),
|
||||
context.call_id.clone(),
|
||||
request.command.clone(),
|
||||
cwd,
|
||||
request.cwd_uri.clone(),
|
||||
request.path_convention,
|
||||
Some(request.process_id.to_string()),
|
||||
transcript,
|
||||
fallback_output,
|
||||
@@ -459,7 +461,8 @@ impl UnifiedExecProcessManager {
|
||||
);
|
||||
let emitter = ToolEmitter::unified_exec(
|
||||
&request.command,
|
||||
cwd.clone(),
|
||||
request.cwd_uri.clone(),
|
||||
request.path_convention,
|
||||
ExecCommandSource::UnifiedExecStartup,
|
||||
Some(request.process_id.to_string()),
|
||||
);
|
||||
@@ -478,6 +481,8 @@ impl UnifiedExecProcessManager {
|
||||
&request.command,
|
||||
request.hook_command.clone(),
|
||||
cwd.clone(),
|
||||
request.cwd_uri.clone(),
|
||||
request.path_convention,
|
||||
start,
|
||||
request.process_id,
|
||||
request.tty,
|
||||
@@ -536,7 +541,6 @@ impl UnifiedExecProcessManager {
|
||||
process_started_alive,
|
||||
context,
|
||||
&request,
|
||||
cwd.clone(),
|
||||
Arc::clone(&transcript),
|
||||
text.clone(),
|
||||
message.clone(),
|
||||
@@ -556,7 +560,6 @@ impl UnifiedExecProcessManager {
|
||||
process_started_alive,
|
||||
context,
|
||||
&request,
|
||||
cwd.clone(),
|
||||
Arc::clone(&transcript),
|
||||
text.clone(),
|
||||
message.clone(),
|
||||
@@ -608,7 +611,6 @@ impl UnifiedExecProcessManager {
|
||||
process_started_alive,
|
||||
context,
|
||||
&request,
|
||||
cwd.clone(),
|
||||
Arc::clone(&transcript),
|
||||
text.clone(),
|
||||
message.clone(),
|
||||
@@ -625,7 +627,8 @@ impl UnifiedExecProcessManager {
|
||||
Arc::clone(&context.turn),
|
||||
context.call_id.clone(),
|
||||
request.command.clone(),
|
||||
cwd.clone(),
|
||||
request.cwd_uri.clone(),
|
||||
request.path_convention,
|
||||
Some(process_id.to_string()),
|
||||
Arc::clone(&transcript),
|
||||
text.clone(),
|
||||
@@ -885,6 +888,8 @@ impl UnifiedExecProcessManager {
|
||||
command: &[String],
|
||||
hook_command: String,
|
||||
cwd: AbsolutePathBuf,
|
||||
cwd_uri: PathUri,
|
||||
path_convention: PathConvention,
|
||||
started_at: Instant,
|
||||
process_id: i32,
|
||||
tty: bool,
|
||||
@@ -923,7 +928,8 @@ impl UnifiedExecProcessManager {
|
||||
Arc::clone(&context.turn),
|
||||
context.call_id.clone(),
|
||||
command.to_vec(),
|
||||
cwd,
|
||||
cwd_uri,
|
||||
path_convention,
|
||||
process_id,
|
||||
transcript,
|
||||
started_at,
|
||||
|
||||
@@ -296,6 +296,7 @@ async fn failed_initial_end_for_unstored_process_uses_fallback_output() {
|
||||
#[allow(deprecated)]
|
||||
cwd: turn.cwd.clone(),
|
||||
cwd_uri: turn_environment.cwd_uri().clone(),
|
||||
path_convention: turn_environment.path_convention(),
|
||||
#[allow(deprecated)]
|
||||
sandbox_cwd: Some(turn.cwd.clone()),
|
||||
environment: turn
|
||||
@@ -322,8 +323,6 @@ async fn failed_initial_end_for_unstored_process_uses_fallback_output() {
|
||||
/*process_started_alive*/ false,
|
||||
&context,
|
||||
&request,
|
||||
#[allow(deprecated)]
|
||||
turn.cwd.clone(),
|
||||
transcript,
|
||||
"PRE_DENIAL_MARKER".to_string(),
|
||||
"Network access denied".to_string(),
|
||||
@@ -345,6 +344,8 @@ async fn failed_initial_end_for_unstored_process_uses_fallback_output() {
|
||||
);
|
||||
assert_eq!(end_event.exit_code, -1);
|
||||
assert_eq!(end_event.process_id.as_deref(), Some("123"));
|
||||
assert_eq!(end_event.cwd, request.cwd_uri);
|
||||
assert_eq!(end_event.path_convention, request.path_convention);
|
||||
assert_eq!(
|
||||
end_event.aggregated_output,
|
||||
"PRE_DENIAL_MARKER\nNetwork access denied"
|
||||
|
||||
Reference in New Issue
Block a user