[codex] Report the exec-server working directory (#29666)

## Summary

- add the exec-server working directory to `environment/info` as an
optional `PathUri`
- populate it from the executor process's current directory
- preserve compatibility with older responses that omit `cwd`

## Why

Remote clients currently have no executor-native default working
directory. This forces callers such as app-server-backend to assume
`/workspace`, which fails for laptop environments. Reporting the cwd
alongside the detected shell lets clients use the path convention and
location of the actual executor.

## Impact

This is backward-compatible: the new response field is optional, and
clients can continue handling responses from older exec servers. A
follow-up app-server-backend change will consume the value for cwd-less
`command/exec` requests.

## Validation

- `just test -p codex-exec-server` (275 passed, 2 skipped)
This commit is contained in:
Rasmus Rygaard
2026-06-23 13:39:13 -07:00
committed by GitHub
Unverified
parent f6500886b7
commit 66f0220c56
2 changed files with 41 additions and 0 deletions
+17
View File
@@ -28,6 +28,7 @@ use crate::remote::NoiseRendezvousEnvironmentConfig;
use crate::remote_file_system::RemoteFileSystem;
use crate::remote_process::RemoteProcess;
use codex_shell_command::shell_detect::DetectedShell;
use codex_utils_path_uri::PathUri;
use tokio_util::task::AbortOnDropHandle;
pub const CODEX_EXEC_SERVER_URL_ENV_VAR: &str = "CODEX_EXEC_SERVER_URL";
@@ -608,6 +609,9 @@ impl EnvironmentInfo {
pub(crate) fn local() -> Self {
Self {
shell: codex_shell_command::shell_detect::default_user_shell().into(),
cwd: std::env::current_dir()
.ok()
.and_then(|cwd| PathUri::from_host_native_path(cwd).ok()),
}
}
}
@@ -655,6 +659,19 @@ mod tests {
assert!(manager.try_local_environment().is_none());
}
#[test]
fn local_environment_info_includes_current_directory() {
let info = super::EnvironmentInfo::local();
assert_eq!(
info.cwd,
Some(
PathUri::from_host_native_path(std::env::current_dir().expect("current directory"))
.expect("cwd URI")
)
);
}
#[tokio::test]
async fn noise_environment_config_selects_remote_as_default() {
let config = noise_environment_config_from_values(
+24
View File
@@ -72,6 +72,9 @@ pub struct InitializeResponse {
#[serde(rename_all = "camelCase")]
pub struct EnvironmentInfo {
pub shell: ShellInfo,
/// Working directory inherited by the exec-server process.
#[serde(default)]
pub cwd: Option<PathUri>,
}
/// Shell detected for an execution/filesystem environment.
@@ -511,10 +514,12 @@ mod base64_bytes {
#[cfg(test)]
mod tests {
use super::EnvironmentInfo;
use super::ExecParams;
use super::FsReadFileParams;
use super::HttpRequestParams;
use super::ProcessId;
use super::ShellInfo;
use codex_file_system::FileSystemSandboxContext;
use codex_network_proxy::ManagedNetworkSandboxContext;
use codex_protocol::models::PermissionProfile;
@@ -566,6 +571,25 @@ mod tests {
assert_eq!(legacy.managed_network, None);
}
#[test]
fn environment_info_accepts_legacy_response_without_cwd() {
let info: EnvironmentInfo = serde_json::from_value(serde_json::json!({
"shell": { "name": "zsh", "path": "/bin/zsh" }
}))
.expect("legacy environment info should deserialize");
assert_eq!(
info,
EnvironmentInfo {
shell: ShellInfo {
name: "zsh".to_string(),
path: "/bin/zsh".to_string(),
},
cwd: None,
}
);
}
#[test]
fn filesystem_protocol_accepts_legacy_absolute_paths_and_serializes_path_uris() {
let legacy_path = std::env::current_dir()