Carry shell invocation metadata and report executor PATH directories (#49360)

## What changed

- Carry the selected shell and login mode through unified exec using `ShellInvocation`. Use this metadata for shell snapshots and credential brokerage instead of inferring it from command arguments.
- Add `prependPathDirs` to exec-server environment metadata, populated from the executor's package PATH directory. Default to an empty list for older servers and omit empty lists from serialized output.

## Testing

Extend environment metadata round-trip coverage for `prependPathDirs` and the remote exec-server integration test to verify the packaged `codex-path` directory is reported.

GitOrigin-RevId: c571c068effcb305d196a701ad9a335ec7a3f5a2
This commit is contained in:
Adam Perry @ OpenAI
2026-09-29 20:40:21 +00:00
committed by copyberry
parent 1983c48fd1
commit 995138d71a
13 changed files with 107 additions and 56 deletions
+2 -2
View File
@@ -43,7 +43,7 @@ use crate::session::session::Session;
use crate::session::step_context::StepContext;
use crate::session::turn_context::TurnContext;
use crate::session::turn_context::TurnEnvironment;
use crate::shell::ShellType;
use crate::shell::ShellInvocation;
use crate::tools::network_approval::DeferredNetworkApproval;
use codex_core_plugins::PluginMetricsSidecar;
@@ -113,7 +113,7 @@ impl UnifiedExecContext {
#[derive(Debug)]
pub(crate) struct ExecCommandRequest {
pub command: Vec<String>,
pub shell_type: ShellType,
pub shell: ShellInvocation,
pub hook_command: String,
pub process_id: i32,
pub yield_time_ms: u64,
@@ -1505,7 +1505,7 @@ impl UnifiedExecProcessManager {
.await;
let req = UnifiedExecToolRequest {
command: request.command.clone(),
shell_type: request.shell_type,
shell: request.shell.clone(),
hook_command: request.hook_command.clone(),
process_id: request.process_id,
cwd,
@@ -1,4 +1,7 @@
use super::*;
use crate::shell::Shell;
use crate::shell::ShellInvocation;
use crate::shell::ShellType;
use crate::unified_exec::clamp_yield_time;
use codex_network_proxy::ManagedNetworkSandboxContext;
use core_test_support::assert_regex_match;
@@ -459,7 +462,13 @@ async fn failed_initial_end_for_unstored_process_uses_fallback_output() {
"-lc".to_string(),
"echo before".to_string(),
],
shell_type: crate::shell::ShellType::Sh,
shell: ShellInvocation {
shell: Shell {
shell_type: ShellType::Sh,
shell_path: "sh".into(),
},
use_login_shell: true,
},
hook_command: "echo before".to_string(),
process_id: 123,
yield_time_ms: 1000,
@@ -179,15 +179,12 @@ pub(super) fn shell_snapshot_request(
|| !request.turn_environment.shell_snapshot_v2_supported
|| request.turn_environment.selection.cwd != *cwd
|| !matches!(request.shell_mode, UnifiedExecShellMode::Direct)
|| !matches!(
request.shell_type,
ShellType::Bash | ShellType::Zsh | ShellType::Sh
)
|| request.command.get(1).is_none_or(|flag| flag != "-lc")
|| !request.shell.is_posix_login()
{
return None;
}
let shell = &request.shell.shell;
Some(ShellSnapshotRequest {
scope_id: format!(
"{}:{}",
@@ -195,8 +192,8 @@ pub(super) fn shell_snapshot_request(
request.turn_environment.selection.environment_id
),
shell: ShellInfo {
name: request.shell_type.name().to_string(),
path: request.command.first()?.clone(),
name: shell.name().to_string(),
path: shell.shell_path.to_string_lossy().into_owned(),
},
})
}