Use selected environment cwd for filesystem helpers (#22542)

## Why

`TurnContext::cwd` is deprecated in favor of resolving paths from the
selected turn environment cwd. A few filesystem-oriented paths were
still constructing sandbox context from the legacy cwd and then mutating
it afterward, or resolving local file paths through the deprecated
helper.

## What changed

- Make `TurnContext::file_system_sandbox_context` take the trusted cwd
explicitly.
- Pass the selected turn environment cwd directly from `apply_patch` and
`view_image` call sites.
- Restrict `spawn_agents_on_csv` to exactly one local environment and
resolve input/output CSV paths from that local environment cwd.
- Remove a redundant test setup assignment that only synchronized
deprecated `TurnContext::cwd` with a replaced config.

## Validation

- `cargo test -p codex-core view_image`
- `cargo test -p codex-core
maybe_persist_mcp_tool_approval_writes_project_config_for_project_server`
- `cargo test -p codex-core parse_csv_supports_quotes_and_commas`
- `git diff --check`
This commit is contained in:
pakrym-oai
2026-05-13 13:18:56 -07:00
committed by GitHub
parent d666238b40
commit 3ac1d15598
5 changed files with 26 additions and 22 deletions

View File

@@ -2233,10 +2233,6 @@ async fn maybe_persist_mcp_tool_approval_writes_project_config_for_project_serve
.build()
.await
.expect("load project config");
#[allow(deprecated)]
{
turn_context.cwd = config.cwd.clone();
}
turn_context.config = Arc::new(config);
let key = McpToolApprovalKey {
server: "docs".to_string(),

View File

@@ -301,6 +301,7 @@ impl TurnContext {
pub(crate) fn file_system_sandbox_context(
&self,
additional_permissions: Option<AdditionalPermissionProfile>,
cwd: &AbsolutePathBuf,
) -> FileSystemSandboxContext {
let (base_file_system_sandbox_policy, base_network_sandbox_policy) =
self.permission_profile.to_runtime_permissions();
@@ -319,8 +320,7 @@ impl TurnContext {
);
FileSystemSandboxContext {
permissions,
#[allow(deprecated)]
cwd: Some(self.cwd.clone()),
cwd: Some(cwd.clone()),
windows_sandbox_level: self.windows_sandbox_level,
windows_sandbox_private_desktop: self
.config

View File

@@ -7,6 +7,7 @@ use crate::tools::registry::ToolExecutor;
use crate::tools::registry::ToolHandler;
use codex_tools::ToolName;
use codex_tools::ToolSpec;
use codex_utils_absolute_path::AbsolutePathBuf;
use super::*;
@@ -67,9 +68,9 @@ pub async fn handle(
));
}
let cwd = single_local_environment_cwd(&turn)?;
let db = required_state_db(&session)?;
#[allow(deprecated)]
let input_path = turn.resolve_path(Some(args.csv_path));
let input_path = cwd.join(args.csv_path);
let input_path_display = input_path.display().to_string();
let csv_content = tokio::fs::read_to_string(&input_path)
.await
@@ -142,10 +143,7 @@ pub async fn handle(
let job_id = Uuid::new_v4().to_string();
let output_csv_path = args.output_csv_path.map_or_else(
|| default_output_csv_path(&input_path, job_id.as_str()),
|path| {
#[allow(deprecated)]
turn.resolve_path(Some(path))
},
|path| cwd.join(path),
);
let job_suffix = &job_id[..8];
let job_name = format!("agent-job-{job_suffix}");
@@ -290,3 +288,19 @@ pub async fn handle(
})?;
Ok(FunctionToolOutput::from_text(content, Some(true)))
}
fn single_local_environment_cwd(turn: &TurnContext) -> Result<&AbsolutePathBuf, FunctionCallError> {
let [turn_environment] = turn.environments.turn_environments.as_slice() else {
return Err(FunctionCallError::RespondToModel(
"spawn_agents_on_csv requires exactly one local environment".to_string(),
));
};
if turn_environment.environment.is_remote() {
return Err(FunctionCallError::RespondToModel(
"spawn_agents_on_csv is not supported for remote environments".to_string(),
));
}
Ok(&turn_environment.cwd)
}

View File

@@ -345,8 +345,7 @@ impl ToolExecutor<ToolInvocation> for ApplyPatchHandler {
};
let cwd = turn_environment.cwd.clone();
let fs = turn_environment.environment.get_filesystem();
let mut sandbox = turn.file_system_sandbox_context(/*additional_permissions*/ None);
sandbox.cwd = Some(cwd.clone());
let sandbox = turn.file_system_sandbox_context(/*additional_permissions*/ None, &cwd);
match codex_apply_patch::verify_apply_patch_args(args, &cwd, fs.as_ref(), Some(&sandbox))
.await
{
@@ -499,8 +498,7 @@ pub(crate) async fn intercept_apply_patch(
call_id: &str,
tool_name: &str,
) -> Result<Option<FunctionToolOutput>, FunctionCallError> {
let mut sandbox = turn.file_system_sandbox_context(/*additional_permissions*/ None);
sandbox.cwd = Some(cwd.clone());
let sandbox = turn.file_system_sandbox_context(/*additional_permissions*/ None, cwd);
match codex_apply_patch::maybe_parse_apply_patch_verified(command, cwd, fs, Some(&sandbox))
.await
{

View File

@@ -134,8 +134,7 @@ impl ToolExecutor<ToolInvocation> for ViewImageHandler {
};
let cwd = turn_environment.cwd.clone();
let abs_path = cwd.join(path);
let mut sandbox = turn.file_system_sandbox_context(/*additional_permissions*/ None);
sandbox.cwd = Some(cwd.clone());
let sandbox = turn.file_system_sandbox_context(/*additional_permissions*/ None, &cwd);
let fs = turn_environment.environment.get_filesystem();
let metadata = fs
@@ -282,10 +281,7 @@ mod tests {
let (session, mut turn) = make_session_and_context().await;
let image_dir = tempfile::tempdir().expect("create image temp dir");
let image_cwd = image_dir.abs();
#[allow(deprecated)]
{
turn.cwd = image_cwd.clone();
}
turn.environments
.turn_environments
.first_mut()