shell-command: use selected local environment cwd

This commit is contained in:
Adam Perry
2026-06-13 05:03:51 +00:00
parent 4c45a95cf6
commit 9b9966b38f
2 changed files with 57 additions and 7 deletions

View File

@@ -9,6 +9,7 @@ use crate::exec_env::create_env;
use crate::function_tool::FunctionCallError;
use crate::maybe_emit_implicit_skill_invocation;
use crate::session::turn_context::TurnContext;
use crate::session::turn_context::TurnEnvironment;
use crate::shell::Shell;
use crate::tools::context::ToolInvocation;
use crate::tools::context::ToolPayload;
@@ -50,6 +51,19 @@ pub(crate) struct ShellCommandHandlerOptions {
}
impl ShellCommandHandler {
fn compatible_execution_cwd(
turn_context: &TurnContext,
) -> codex_utils_absolute_path::AbsolutePathBuf {
turn_context
.environments
.primary()
.and_then(TurnEnvironment::compatible_cwd)
.unwrap_or_else(|| {
#[allow(deprecated)]
turn_context.cwd.clone()
})
}
pub(crate) fn new(options: ShellCommandHandlerOptions) -> Self {
let backend = match options.backend_config {
ShellCommandBackendConfig::Classic => ShellCommandBackend::Classic,
@@ -92,8 +106,11 @@ impl ShellCommandHandler {
let shell = session.user_shell();
let use_login_shell = Self::resolve_use_login_shell(params.login, allow_login_shell)?;
let command = Self::base_command(shell.as_ref(), &params.command, use_login_shell);
#[allow(deprecated)]
let cwd = turn_context.resolve_path(params.workdir.clone());
let base_cwd = Self::compatible_execution_cwd(turn_context);
let cwd = params
.workdir
.as_ref()
.map_or(base_cwd.clone(), |workdir| base_cwd.join(workdir));
Ok(ExecParams {
command,
@@ -176,16 +193,14 @@ impl ShellCommandHandler {
));
}
#[allow(deprecated)]
let cwd = resolve_workdir_base_path(&arguments, &turn.cwd)?;
let base_cwd = Self::compatible_execution_cwd(turn.as_ref());
let cwd = resolve_workdir_base_path(&arguments, &base_cwd)?;
let params: ShellCommandToolCallParams = parse_arguments_with_base_path(&arguments, &cwd)?;
#[allow(deprecated)]
let workdir = turn.resolve_path(params.workdir.clone());
maybe_emit_implicit_skill_invocation(
session.as_ref(),
turn.as_ref(),
&params.command,
&workdir,
&cwd,
)
.await;
let prefix_rule = params.prefix_rule.clone();

View File

@@ -130,6 +130,41 @@ async fn shell_command_handler_to_exec_params_uses_session_shell_and_turn_contex
assert_eq!(exec_params.arg0, None);
}
#[tokio::test]
async fn shell_command_handler_uses_selected_local_environment_cwd() {
let (session, mut turn_context) = make_session_and_context().await;
let selected = turn_context.environments.turn_environments[0].clone();
let selected_dir = tempfile::tempdir().expect("selected cwd");
let selected_cwd = selected_dir.path().to_path_buf().abs();
turn_context.environments.turn_environments[0] = TurnEnvironment::new(
selected.environment_id,
selected.environment,
selected_cwd.clone(),
selected.shell,
);
let params = ShellCommandToolCallParams {
command: "echo hello".to_string(),
workdir: Some("subdir".to_string()),
login: None,
timeout_ms: None,
sandbox_permissions: None,
additional_permissions: None,
prefix_rule: None,
justification: None,
};
let exec_params = ShellCommandHandler::to_exec_params(
&params,
&session,
&turn_context,
session.thread_id,
/*allow_login_shell*/ false,
)
.expect("selected local environment should be executable");
assert_eq!(exec_params.cwd, selected_cwd.join("subdir"));
}
#[test]
fn shell_command_handler_respects_explicit_login_flag() {
let (_tx, shell_snapshot) = watch::channel(Some(Arc::new(ShellSnapshot {