[codex] exec-server honors remote environment cwd and shell (#28122)

## Why

Next slice needed to make progress on the `remote_env_windows` test is
to support passing a Windows cwd for the remote environment and using
that environment's native shell. This lets the test run a real Windows
process instead of only recording an early path or shell mismatch.

## What

- change `TurnEnvironmentSelection.cwd` from `AbsolutePathBuf` to
`PathUri`
- convert local cwd values to URIs when constructing selections
- preserve a remote primary cwd instead of replacing it with the local
legacy fallback
- prefer the selected environment's discovered shell for unified exec,
falling back to the session shell when unavailable
- convert back to a host-native absolute path at current native-only
consumer boundaries
- reject or deny unsupported foreign cwd values at the existing
request-permissions boundary, with TODOs for its future migration
- extend the hermetic Wine test to execute Windows PowerShell in
`C:\windows` and verify successful process completion
- record the current app-server rejection against the same Wine-backed
remote Windows fixture when its cwd is supplied as a native Windows path
This commit is contained in:
Adam Perry @ OpenAI
2026-06-13 23:07:46 -07:00
committed by GitHub
Unverified
parent 5e9249ec02
commit efbd00f21f
22 changed files with 300 additions and 96 deletions
+36 -8
View File
@@ -149,6 +149,7 @@ use codex_thread_store::ResumeThreadParams;
use codex_thread_store::ThreadPersistenceMetadata;
use codex_thread_store::ThreadStore;
use codex_utils_output_truncation::TruncationPolicy;
use codex_utils_path_uri::PathUri;
use futures::future::BoxFuture;
use futures::future::Shared;
use futures::prelude::*;
@@ -2206,6 +2207,18 @@ impl Session {
}
let requested_permissions = args.permissions;
// TODO(anp): Migrate request_permissions to support paths from foreign environments.
let Ok(native_environment_cwd) = environment.cwd.to_abs_path() else {
warn!(
cwd = %environment.cwd,
"request_permissions requires a cwd native to the Codex host"
);
return Some(RequestPermissionsResponse {
permissions: RequestPermissionProfile::default(),
scope: PermissionGrantScope::Turn,
strict_auto_review: false,
});
};
if crate::guardian::routes_approval_to_guardian(turn_context.as_ref()) {
let originating_turn_state = {
@@ -2273,7 +2286,7 @@ impl Session {
let response = Self::normalize_request_permissions_response(
requested_permissions,
response,
environment.cwd.as_path(),
native_environment_cwd.as_path(),
);
self.record_granted_request_permissions_for_turn(
&response,
@@ -2313,7 +2326,7 @@ impl Session {
started_at_ms: now_unix_timestamp_ms(),
reason: args.reason,
permissions: requested_permissions,
cwd: Some(environment.cwd),
cwd: Some(native_environment_cwd),
});
self.send_event(turn_context.as_ref(), event).await;
tokio::select! {
@@ -2354,7 +2367,7 @@ impl Session {
});
};
let mut environment = turn_environment.selection();
environment.cwd = cwd;
environment.cwd = PathUri::from_abs_path(&cwd);
self.request_permissions_for_environment(
turn_context,
call_id,
@@ -2457,11 +2470,26 @@ impl Session {
};
match entry {
Some(entry) => {
let response = Self::normalize_request_permissions_response(
entry.requested_permissions,
response,
entry.environment.cwd.as_path(),
);
// TODO(anp): Migrate request_permissions to support paths from foreign environments.
let response = match entry.environment.cwd.to_abs_path() {
Ok(native_environment_cwd) => Self::normalize_request_permissions_response(
entry.requested_permissions,
response,
native_environment_cwd.as_path(),
),
Err(err) => {
warn!(
cwd = %entry.environment.cwd,
%err,
"request_permissions requires a cwd native to the Codex host"
);
RequestPermissionsResponse {
permissions: RequestPermissionProfile::default(),
scope: PermissionGrantScope::Turn,
strict_auto_review: false,
}
}
};
self.record_granted_request_permissions_for_turn(
&response,
&entry.environment.environment_id,
+2 -1
View File
@@ -58,6 +58,7 @@ use codex_protocol::protocol::SandboxPolicy;
use codex_protocol::protocol::TurnEnvironmentSelections;
use codex_protocol::request_permissions::PermissionGrantScope;
use codex_protocol::request_permissions::RequestPermissionProfile;
use codex_utils_path_uri::PathUri;
use tracing::Span;
use crate::rollout::recorder::RolloutRecorder;
@@ -4705,7 +4706,7 @@ async fn cwd_update_rewrites_sticky_environment_cwd() {
assert_eq!(state.session_configuration.cwd(), &updated_cwd);
assert_eq!(
state.session_configuration.environment_selections()[0].cwd,
updated_cwd
PathUri::from_abs_path(&updated_cwd)
);
assert_ne!(environment_cwd, updated_cwd);
}
+1 -1
View File
@@ -79,7 +79,7 @@ impl TurnEnvironment {
pub(crate) fn selection(&self) -> TurnEnvironmentSelection {
TurnEnvironmentSelection {
environment_id: self.environment_id.clone(),
cwd: self.cwd.clone(),
cwd: self.cwd_uri.clone(),
}
}
}