Use AbsolutePathBuf for cwd state (#15710)

Migrate `cwd` and related session/config state to `AbsolutePathBuf` so
downstream consumers consistently see absolute working directories.

Add test-only `.abs()` helpers for `Path`, `PathBuf`, and `TempDir`, and
update branch-local tests to use them instead of
`AbsolutePathBuf::try_from(...)`.

For the remaining TUI/app-server snapshot coverage that renders absolute
cwd values, keep the snapshots unchanged and skip the Windows-only cases
where the platform-specific absolute path layout differs.
This commit is contained in:
pakrym-oai
2026-03-25 09:02:22 -07:00
committed by GitHub
parent 178c3b15b4
commit 504aeb0e09
65 changed files with 717 additions and 422 deletions

View File

@@ -89,7 +89,7 @@ impl ToolHandler for ArtifactsHandler {
let result = client
.execute_build(ArtifactBuildRequest {
source: args.source,
cwd: turn.cwd.clone(),
cwd: turn.cwd.to_path_buf(),
timeout: Some(Duration::from_millis(
args.timeout_ms
.unwrap_or(DEFAULT_EXECUTION_TIMEOUT.as_millis() as u64),
@@ -221,7 +221,7 @@ fn default_runtime_manager(codex_home: std::path::PathBuf) -> ArtifactRuntimeMan
async fn emit_exec_begin(session: &Session, turn: &TurnContext, call_id: &str) {
let emitter = ToolEmitter::shell(
vec![ARTIFACTS_TOOL_NAME.to_string()],
turn.cwd.clone(),
turn.cwd.to_path_buf(),
ExecCommandSource::Agent,
/*freeform*/ true,
);
@@ -247,7 +247,7 @@ async fn emit_exec_end(
};
let emitter = ToolEmitter::shell(
vec![ARTIFACTS_TOOL_NAME.to_string()],
turn.cwd.clone(),
turn.cwd.to_path_buf(),
ExecCommandSource::Agent,
/*freeform*/ true,
);

View File

@@ -61,7 +61,7 @@ async fn emit_js_repl_exec_begin(
) {
let emitter = ToolEmitter::shell(
vec!["js_repl".to_string()],
turn.cwd.clone(),
turn.cwd.to_path_buf(),
ExecCommandSource::Agent,
/*freeform*/ false,
);
@@ -80,7 +80,7 @@ async fn emit_js_repl_exec_end(
let exec_output = build_js_repl_exec_output(output, error, duration);
let emitter = ToolEmitter::shell(
vec!["js_repl".to_string()],
turn.cwd.clone(),
turn.cwd.to_path_buf(),
ExecCommandSource::Agent,
/*freeform*/ false,
);

View File

@@ -77,7 +77,7 @@ async fn emit_js_repl_exec_end_sends_event() {
assert_eq!(event.call_id, "call-1");
assert_eq!(event.turn_id, turn.sub_id);
assert_eq!(event.command, vec!["js_repl".to_string()]);
assert_eq!(event.cwd, turn.cwd);
assert_eq!(event.cwd, turn.cwd.to_path_buf());
assert_eq!(event.source, ExecCommandSource::Agent);
assert_eq!(event.interaction_input, None);
assert_eq!(event.stdout, "hello");

View File

@@ -39,6 +39,7 @@ use codex_protocol::protocol::InitialHistory;
use codex_protocol::protocol::InterAgentCommunication;
use codex_protocol::protocol::RolloutItem;
use codex_protocol::user_input::UserInput;
use core_test_support::TempDirExt;
use pretty_assertions::assert_eq;
use serde::Deserialize;
use serde_json::json;
@@ -2236,7 +2237,7 @@ async fn build_agent_spawn_config_uses_turn_context_values() {
..ShellEnvironmentPolicy::default()
};
let temp_dir = tempfile::tempdir().expect("temp dir");
turn.cwd = temp_dir.path().to_path_buf();
turn.cwd = temp_dir.abs();
turn.codex_linux_sandbox_exe = Some(PathBuf::from("/bin/echo"));
let sandbox_policy = pick_allowed_sandbox_policy(
&turn.config.permissions.sandbox_policy,

View File

@@ -1050,7 +1050,7 @@ impl JsReplManager {
"--experimental-vm-modules".to_string(),
kernel_path.to_string_lossy().to_string(),
],
cwd: turn.cwd.clone(),
cwd: turn.cwd.to_path_buf(),
env,
additional_permissions: None,
};

View File

@@ -14,6 +14,8 @@ use codex_protocol::models::FunctionCallOutputPayload;
use codex_protocol::models::ImageDetail;
use codex_protocol::models::ResponseInputItem;
use codex_protocol::openai_models::InputModality;
use core_test_support::PathBufExt;
use core_test_support::TempDirExt;
use pretty_assertions::assert_eq;
use std::fs;
use std::path::Path;
@@ -739,7 +741,7 @@ async fn interrupt_active_exec_stops_aborted_kernel_before_later_exec() -> anyho
let dir = tempdir()?;
let (session, mut turn) = make_session_and_context().await;
turn.cwd = dir.path().to_path_buf();
turn.cwd = dir.abs();
set_danger_full_access(&mut turn);
let session = Arc::new(session);
let turn = Arc::new(turn);
@@ -1017,7 +1019,7 @@ async fn js_repl_waits_for_unawaited_tool_calls_before_completion() -> anyhow::R
let marker = turn
.cwd
.join(format!("js-repl-unawaited-marker-{}.txt", Uuid::new_v4()));
.join(format!("js-repl-unawaited-marker-{}.txt", Uuid::new_v4()))?;
let marker_json = serde_json::to_string(&marker.to_string_lossy().to_string())?;
let result = manager
.execute(
@@ -1062,10 +1064,10 @@ async fn js_repl_persisted_tool_helpers_work_across_cells() -> anyhow::Result<()
let global_marker = turn
.cwd
.join(format!("js-repl-global-helper-{}.txt", Uuid::new_v4()));
.join(format!("js-repl-global-helper-{}.txt", Uuid::new_v4()))?;
let lexical_marker = turn
.cwd
.join(format!("js-repl-lexical-helper-{}.txt", Uuid::new_v4()));
.join(format!("js-repl-lexical-helper-{}.txt", Uuid::new_v4()))?;
let global_marker_json = serde_json::to_string(&global_marker.to_string_lossy().to_string())?;
let lexical_marker_json = serde_json::to_string(&lexical_marker.to_string_lossy().to_string())?;
@@ -2101,7 +2103,7 @@ async fn js_repl_prefers_env_node_module_dirs_over_config() -> anyhow::Result<()
"CODEX_JS_REPL_NODE_MODULE_DIRS".to_string(),
env_base.path().to_string_lossy().to_string(),
);
turn.cwd = cwd_dir.path().to_path_buf();
turn.cwd = cwd_dir.abs();
turn.js_repl = Arc::new(JsReplHandle::with_node_path(
turn.config.js_repl_node_path.clone(),
vec![config_base.path().to_path_buf()],
@@ -2145,7 +2147,7 @@ async fn js_repl_resolves_from_first_config_dir() -> anyhow::Result<()> {
turn.shell_environment_policy
.r#set
.remove("CODEX_JS_REPL_NODE_MODULE_DIRS");
turn.cwd = cwd_dir.path().to_path_buf();
turn.cwd = cwd_dir.abs();
turn.js_repl = Arc::new(JsReplHandle::with_node_path(
turn.config.js_repl_node_path.clone(),
vec![
@@ -2189,7 +2191,7 @@ async fn js_repl_falls_back_to_cwd_node_modules() -> anyhow::Result<()> {
turn.shell_environment_policy
.r#set
.remove("CODEX_JS_REPL_NODE_MODULE_DIRS");
turn.cwd = cwd_dir.path().to_path_buf();
turn.cwd = cwd_dir.abs();
turn.js_repl = Arc::new(JsReplHandle::with_node_path(
turn.config.js_repl_node_path.clone(),
vec![config_base.path().to_path_buf()],
@@ -2230,7 +2232,7 @@ async fn js_repl_accepts_node_modules_dir_entries() -> anyhow::Result<()> {
turn.shell_environment_policy
.r#set
.remove("CODEX_JS_REPL_NODE_MODULE_DIRS");
turn.cwd = cwd_dir.path().to_path_buf();
turn.cwd = cwd_dir.abs();
turn.js_repl = Arc::new(JsReplHandle::with_node_path(
turn.config.js_repl_node_path.clone(),
vec![base_dir.path().join("node_modules")],
@@ -2284,7 +2286,7 @@ async fn js_repl_supports_relative_file_imports() -> anyhow::Result<()> {
turn.shell_environment_policy
.r#set
.remove("CODEX_JS_REPL_NODE_MODULE_DIRS");
turn.cwd = cwd_dir.path().to_path_buf();
turn.cwd = cwd_dir.abs();
turn.js_repl = Arc::new(JsReplHandle::with_node_path(
turn.config.js_repl_node_path.clone(),
Vec::new(),
@@ -2331,7 +2333,7 @@ async fn js_repl_supports_absolute_file_imports() -> anyhow::Result<()> {
turn.shell_environment_policy
.r#set
.remove("CODEX_JS_REPL_NODE_MODULE_DIRS");
turn.cwd = cwd_dir.path().to_path_buf();
turn.cwd = cwd_dir.abs();
turn.js_repl = Arc::new(JsReplHandle::with_node_path(
turn.config.js_repl_node_path.clone(),
Vec::new(),
@@ -2385,7 +2387,7 @@ async fn js_repl_imported_local_files_can_access_repl_globals() -> anyhow::Resul
turn.shell_environment_policy
.r#set
.remove("CODEX_JS_REPL_NODE_MODULE_DIRS");
turn.cwd = cwd_dir.path().to_path_buf();
turn.cwd = cwd_dir.abs();
turn.js_repl = Arc::new(JsReplHandle::with_node_path(
turn.config.js_repl_node_path.clone(),
Vec::new(),
@@ -2429,7 +2431,7 @@ async fn js_repl_reimports_local_files_after_edit() -> anyhow::Result<()> {
turn.shell_environment_policy
.r#set
.remove("CODEX_JS_REPL_NODE_MODULE_DIRS");
turn.cwd = cwd_dir.path().to_path_buf();
turn.cwd = cwd_dir.abs();
turn.js_repl = Arc::new(JsReplHandle::with_node_path(
turn.config.js_repl_node_path.clone(),
Vec::new(),
@@ -2485,7 +2487,7 @@ async fn js_repl_reimports_local_files_after_fixing_failure() -> anyhow::Result<
turn.shell_environment_policy
.r#set
.remove("CODEX_JS_REPL_NODE_MODULE_DIRS");
turn.cwd = cwd_dir.path().to_path_buf();
turn.cwd = cwd_dir.abs();
turn.js_repl = Arc::new(JsReplHandle::with_node_path(
turn.config.js_repl_node_path.clone(),
Vec::new(),
@@ -2563,7 +2565,7 @@ async fn js_repl_local_files_expose_node_like_import_meta() -> anyhow::Result<()
turn.shell_environment_policy
.r#set
.remove("CODEX_JS_REPL_NODE_MODULE_DIRS");
turn.cwd = cwd_dir.path().to_path_buf();
turn.cwd = cwd_dir.abs();
turn.js_repl = Arc::new(JsReplHandle::with_node_path(
turn.config.js_repl_node_path.clone(),
Vec::new(),
@@ -2648,7 +2650,7 @@ async fn js_repl_local_files_reject_static_bare_imports() -> anyhow::Result<()>
turn.shell_environment_policy
.r#set
.remove("CODEX_JS_REPL_NODE_MODULE_DIRS");
turn.cwd = cwd_dir.path().to_path_buf();
turn.cwd = cwd_dir.abs();
turn.js_repl = Arc::new(JsReplHandle::with_node_path(
turn.config.js_repl_node_path.clone(),
Vec::new(),
@@ -2693,7 +2695,7 @@ async fn js_repl_rejects_unsupported_file_specifiers() -> anyhow::Result<()> {
turn.shell_environment_policy
.r#set
.remove("CODEX_JS_REPL_NODE_MODULE_DIRS");
turn.cwd = cwd_dir.path().to_path_buf();
turn.cwd = cwd_dir.abs();
turn.js_repl = Arc::new(JsReplHandle::with_node_path(
turn.config.js_repl_node_path.clone(),
Vec::new(),
@@ -2795,7 +2797,7 @@ async fn js_repl_blocks_sensitive_builtin_imports_from_local_files() -> anyhow::
turn.shell_environment_policy
.r#set
.remove("CODEX_JS_REPL_NODE_MODULE_DIRS");
turn.cwd = cwd_dir.path().to_path_buf();
turn.cwd = cwd_dir.abs();
turn.js_repl = Arc::new(JsReplHandle::with_node_path(
turn.config.js_repl_node_path.clone(),
Vec::new(),
@@ -2845,7 +2847,7 @@ async fn js_repl_local_files_do_not_escape_node_module_search_roots() -> anyhow:
turn.shell_environment_policy
.r#set
.remove("CODEX_JS_REPL_NODE_MODULE_DIRS");
turn.cwd = cwd_dir.clone();
turn.cwd = cwd_dir.abs();
turn.js_repl = Arc::new(JsReplHandle::with_node_path(
turn.config.js_repl_node_path.clone(),
Vec::new(),

View File

@@ -379,7 +379,7 @@ impl NetworkApprovalService {
approval_id,
/*approval_id*/ None,
prompt_command,
turn_context.cwd.clone(),
turn_context.cwd.to_path_buf(),
Some(prompt_reason),
Some(network_approval_context.clone()),
/*proposed_execpolicy_amendment*/ None,

View File

@@ -530,7 +530,7 @@ async fn dispatch_after_tool_use_hook(
.hooks()
.dispatch(HookPayload {
session_id: session.conversation_id,
cwd: turn.cwd.clone(),
cwd: turn.cwd.to_path_buf(),
client: turn.app_server_client_name.clone(),
triggered_at: chrono::Utc::now(),
hook_event: HookEvent::AfterToolUse {

View File

@@ -159,7 +159,7 @@ pub(super) async fn try_run_zsh_fork(
network: sandbox_network,
windows_sandbox_level,
arg0,
sandbox_policy_cwd: ctx.turn.cwd.clone(),
sandbox_policy_cwd: ctx.turn.cwd.to_path_buf(),
macos_seatbelt_profile_extensions: ctx
.turn
.config
@@ -263,7 +263,7 @@ pub(crate) async fn prepare_unified_exec_zsh_fork(
network: exec_request.network.clone(),
windows_sandbox_level: exec_request.windows_sandbox_level,
arg0: exec_request.arg0.clone(),
sandbox_policy_cwd: ctx.turn.cwd.clone(),
sandbox_policy_cwd: ctx.turn.cwd.to_path_buf(),
macos_seatbelt_profile_extensions: ctx
.turn
.config