From 4454e1411b82dd755e487e4b7ecd4d949c0ed336 Mon Sep 17 00:00:00 2001 From: pakrym-oai Date: Wed, 13 May 2026 11:15:25 -0700 Subject: [PATCH] Deprecate TurnContext cwd and resolve_path (#22519) ## Why `TurnContext::cwd` and `TurnContext::resolve_path` are being phased out in favor of using the selected turn environment cwd directly. Deprecating both APIs makes any new direct dependency visible while preserving the existing migration path for current callers. ## What Changed - Marked `TurnContext::cwd` and `TurnContext::resolve_path` as deprecated with guidance to use the selected turn environment cwd instead. - Added exact `#[allow(deprecated)]` suppressions at each existing direct usage site, including tests, rather than adding crate-wide suppression. - Kept the change behavior-preserving: current cwd reads, writes, and path resolution continue to use the same values. ## Verification - `just fmt` - `cargo check -p codex-core` - `cargo check -p codex-core --tests` - `git diff --check` --- codex-rs/core/src/codex_delegate.rs | 11 ++++- codex-rs/core/src/codex_delegate_tests.rs | 1 + codex-rs/core/src/context_manager/updates.rs | 1 + codex-rs/core/src/guardian/review_session.rs | 2 + codex-rs/core/src/hook_runtime.rs | 7 +++ codex-rs/core/src/mcp_openai_file.rs | 16 +++++-- codex-rs/core/src/mcp_tool_call.rs | 1 + codex-rs/core/src/mcp_tool_call_tests.rs | 18 ++++++-- codex-rs/core/src/memory_usage.rs | 21 ++++----- codex-rs/core/src/session/handlers.rs | 1 + codex-rs/core/src/session/mcp.rs | 1 + codex-rs/core/src/session/mod.rs | 3 ++ codex-rs/core/src/session/review.rs | 2 + .../session/rollout_reconstruction_tests.rs | 8 ++++ codex-rs/core/src/session/tests.rs | 45 ++++++++++++++----- .../core/src/session/tests/guardian_tests.rs | 12 +++-- codex-rs/core/src/session/turn.rs | 5 +++ codex-rs/core/src/session/turn_context.rs | 9 ++++ codex-rs/core/src/tasks/user_shell.rs | 2 + .../agent_jobs/spawn_agents_on_csv.rs | 6 ++- .../src/tools/handlers/multi_agents_common.rs | 4 +- .../src/tools/handlers/multi_agents_tests.rs | 22 ++++++--- .../src/tools/handlers/request_permissions.rs | 1 + codex-rs/core/src/tools/handlers/shell.rs | 2 + .../src/tools/handlers/shell/shell_command.rs | 6 ++- .../core/src/tools/handlers/shell_tests.rs | 1 + .../core/src/tools/handlers/view_image.rs | 5 ++- codex-rs/core/src/tools/network_approval.rs | 1 + codex-rs/core/src/tools/orchestrator.rs | 1 + codex-rs/core/src/tools/registry.rs | 1 + codex-rs/core/src/unified_exec/mod_tests.rs | 9 +++- .../src/unified_exec/process_manager_tests.rs | 3 ++ 32 files changed, 183 insertions(+), 45 deletions(-) diff --git a/codex-rs/core/src/codex_delegate.rs b/codex-rs/core/src/codex_delegate.rs index 5a01384bf..493771e28 100644 --- a/codex-rs/core/src/codex_delegate.rs +++ b/codex-rs/core/src/codex_delegate.rs @@ -530,7 +530,10 @@ async fn handle_patch_approval( let guardian_decision = if routes_approval_to_guardian(parent_ctx) { let files = changes .keys() - .map(|path| parent_ctx.cwd.join(path)) + .map(|path| { + #[allow(deprecated)] + parent_ctx.cwd.join(path) + }) .collect::>(); let review_cancel = cancel_token.child_token(); let patch = changes @@ -566,6 +569,7 @@ async fn handle_patch_approval( new_guardian_review_id(), GuardianApprovalRequest::ApplyPatch { id: approval_id.clone(), + #[allow(deprecated)] cwd: parent_ctx.cwd.clone(), files, patch, @@ -739,7 +743,10 @@ async fn handle_request_permissions( reason: event.reason, permissions: event.permissions, }; - let cwd = event.cwd.unwrap_or_else(|| parent_ctx.cwd.clone()); + let cwd = event.cwd.unwrap_or_else(|| { + #[allow(deprecated)] + parent_ctx.cwd.clone() + }); let response_fut = parent_session.request_permissions_for_cwd( parent_ctx, call_id.clone(), diff --git a/codex-rs/core/src/codex_delegate_tests.rs b/codex-rs/core/src/codex_delegate_tests.rs index ecd392e3e..66cde8d1e 100644 --- a/codex-rs/core/src/codex_delegate_tests.rs +++ b/codex-rs/core/src/codex_delegate_tests.rs @@ -207,6 +207,7 @@ async fn handle_request_permissions_uses_tool_call_id_for_round_trip() { scope: PermissionGrantScope::Turn, strict_auto_review: false, }; + #[allow(deprecated)] let delegated_cwd = parent_ctx.cwd.join("delegated-cwd"); let cancel_token = CancellationToken::new(); let request_call_id = call_id.clone(); diff --git a/codex-rs/core/src/context_manager/updates.rs b/codex-rs/core/src/context_manager/updates.rs index 0cdbc0b01..d7302bbbf 100644 --- a/codex-rs/core/src/context_manager/updates.rs +++ b/codex-rs/core/src/context_manager/updates.rs @@ -61,6 +61,7 @@ fn build_permissions_update_item( next.approval_policy.value(), next.config.approvals_reviewer, exec_policy, + #[allow(deprecated)] &next.cwd, next.features.enabled(Feature::ExecPermissionApprovals), next.features.enabled(Feature::RequestPermissionsTool), diff --git a/codex-rs/core/src/guardian/review_session.rs b/codex-rs/core/src/guardian/review_session.rs index a419d7cbf..d3fae0f2e 100644 --- a/codex-rs/core/src/guardian/review_session.rs +++ b/codex-rs/core/src/guardian/review_session.rs @@ -708,6 +708,7 @@ async fn run_review_on_session( Box::pin(review_session.codex.submit(Op::UserTurn { environments: None, items: prompt_items.items, + #[allow(deprecated)] cwd: params.parent_turn.cwd.to_path_buf(), approval_policy: AskForApproval::Never, approvals_reviewer: None, @@ -1086,6 +1087,7 @@ mod tests { let reasoning_effort = turn.reasoning_effort; let reasoning_summary = turn.reasoning_summary; let personality = turn.personality; + #[allow(deprecated)] let cwd = turn.cwd.clone(); let spawn_config = build_guardian_review_session_config( turn.config.as_ref(), diff --git a/codex-rs/core/src/hook_runtime.rs b/codex-rs/core/src/hook_runtime.rs index 3d72e4a7c..78fc24833 100644 --- a/codex-rs/core/src/hook_runtime.rs +++ b/codex-rs/core/src/hook_runtime.rs @@ -116,6 +116,7 @@ pub(crate) async fn run_pending_session_start_hooks( let request = codex_hooks::SessionStartRequest { session_id: sess.session_id().into(), + #[allow(deprecated)] cwd: turn_context.cwd.clone(), transcript_path: sess.hook_transcript_path().await, model: turn_context.model_info.slug.clone(), @@ -150,6 +151,7 @@ pub(crate) async fn run_pre_tool_use_hooks( let request = PreToolUseRequest { session_id: sess.session_id().into(), turn_id: turn_context.sub_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.clone(), transcript_path: sess.hook_transcript_path().await, model: turn_context.model_info.slug.clone(), @@ -209,6 +211,7 @@ pub(crate) async fn run_permission_request_hooks( let request = PermissionRequestRequest { session_id: sess.session_id().into(), turn_id: turn_context.sub_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.to_path_buf(), transcript_path: sess.hook_transcript_path().await, model: turn_context.model_info.slug.clone(), @@ -249,6 +252,7 @@ pub(crate) async fn run_post_tool_use_hooks( let request = PostToolUseRequest { session_id: sess.session_id().into(), turn_id: turn_context.sub_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.clone(), transcript_path: sess.hook_transcript_path().await, model: turn_context.model_info.slug.clone(), @@ -276,6 +280,7 @@ pub(crate) async fn run_pre_compact_hooks( let request = codex_hooks::PreCompactRequest { session_id: sess.session_id().into(), turn_id: turn_context.sub_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.clone(), transcript_path: sess.hook_transcript_path().await, model: turn_context.model_info.slug.clone(), @@ -313,6 +318,7 @@ pub(crate) async fn run_post_compact_hooks( let request = codex_hooks::PostCompactRequest { session_id: sess.session_id().into(), turn_id: turn_context.sub_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.clone(), transcript_path: sess.hook_transcript_path().await, model: turn_context.model_info.slug.clone(), @@ -338,6 +344,7 @@ pub(crate) async fn run_user_prompt_submit_hooks( let request = UserPromptSubmitRequest { session_id: sess.session_id().into(), turn_id: turn_context.sub_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.clone(), transcript_path: sess.hook_transcript_path().await, model: turn_context.model_info.slug.clone(), diff --git a/codex-rs/core/src/mcp_openai_file.rs b/codex-rs/core/src/mcp_openai_file.rs index 0e0d4a600..ae44515c6 100644 --- a/codex-rs/core/src/mcp_openai_file.rs +++ b/codex-rs/core/src/mcp_openai_file.rs @@ -102,6 +102,7 @@ async fn build_uploaded_local_argument_value( index: Option, file_path: &str, ) -> Result { + #[allow(deprecated)] let resolved_path = turn_context.resolve_path(Some(file_path.to_string())); let Some(auth) = auth else { return Err( @@ -216,7 +217,10 @@ mod tests { tokio::fs::write(&local_path, b"hello") .await .expect("write local file"); - turn_context.cwd = AbsolutePathBuf::try_from(dir.path()).expect("absolute path"); + #[allow(deprecated)] + { + turn_context.cwd = AbsolutePathBuf::try_from(dir.path()).expect("absolute path"); + } let mut config = (*turn_context.config).clone(); config.chatgpt_base_url = format!("{}/backend-api", server.uri()); @@ -297,7 +301,10 @@ mod tests { tokio::fs::write(&local_path, b"hello") .await .expect("write local file"); - turn_context.cwd = AbsolutePathBuf::try_from(dir.path()).expect("absolute path"); + #[allow(deprecated)] + { + turn_context.cwd = AbsolutePathBuf::try_from(dir.path()).expect("absolute path"); + } let mut config = (*turn_context.config).clone(); config.chatgpt_base_url = format!("{}/backend-api", server.uri()); @@ -411,7 +418,10 @@ mod tests { tokio::fs::write(dir.path().join("two.csv"), b"two") .await .expect("write second local file"); - turn_context.cwd = AbsolutePathBuf::try_from(dir.path()).expect("absolute path"); + #[allow(deprecated)] + { + turn_context.cwd = AbsolutePathBuf::try_from(dir.path()).expect("absolute path"); + } let mut config = (*turn_context.config).clone(); config.chatgpt_base_url = format!("{}/backend-api", server.uri()); diff --git a/codex-rs/core/src/mcp_tool_call.rs b/codex-rs/core/src/mcp_tool_call.rs index 8855aae62..d1faa421d 100644 --- a/codex-rs/core/src/mcp_tool_call.rs +++ b/codex-rs/core/src/mcp_tool_call.rs @@ -732,6 +732,7 @@ async fn augment_mcp_tool_request_meta_with_sandbox_state( permission_profile: Some(turn_context.permission_profile()), sandbox_policy: turn_context.sandbox_policy(), codex_linux_sandbox_exe: turn_context.codex_linux_sandbox_exe.clone(), + #[allow(deprecated)] sandbox_cwd: turn_context.cwd.to_path_buf(), use_legacy_landlock: turn_context.features.use_legacy_landlock(), })?; diff --git a/codex-rs/core/src/mcp_tool_call_tests.rs b/codex-rs/core/src/mcp_tool_call_tests.rs index 65f897728..8b4f1ba22 100644 --- a/codex-rs/core/src/mcp_tool_call_tests.rs +++ b/codex-rs/core/src/mcp_tool_call_tests.rs @@ -1221,7 +1221,10 @@ async fn install_host_owned_codex_apps_manager(session: &Session, turn_context: turn_context.sub_id.clone(), session.get_tx_event(), turn_context.permission_profile(), - codex_mcp::McpRuntimeEnvironment::new(environment, turn_context.cwd.to_path_buf()), + codex_mcp::McpRuntimeEnvironment::new(environment, { + #[allow(deprecated)] + turn_context.cwd.to_path_buf() + }), turn_context.config.codex_home.to_path_buf(), codex_mcp::codex_apps_tools_cache_key(auth.as_ref()), /*host_owned_codex_apps_enabled*/ true, @@ -2230,7 +2233,10 @@ async fn maybe_persist_mcp_tool_approval_writes_project_config_for_project_serve .build() .await .expect("load project config"); - turn_context.cwd = config.cwd.clone(); + #[allow(deprecated)] + { + turn_context.cwd = config.cwd.clone(); + } turn_context.config = Arc::new(config); let key = McpToolApprovalKey { server: "docs".to_string(), @@ -2431,12 +2437,14 @@ async fn permission_request_hook_allows_mcp_tool_call() { .lines() .map(|line| serde_json::from_str::(line).expect("parse hook input")) .collect::>(); + #[allow(deprecated)] + let turn_cwd = turn_context.cwd.clone(); assert_eq!( inputs, vec![serde_json::json!({ "session_id": session.session_id(), "turn_id": "turn_id", - "cwd": turn_context.cwd, + "cwd": turn_cwd, "transcript_path": null, "model": turn_context.model_info.slug, "permission_mode": "default", @@ -2491,12 +2499,14 @@ async fn permission_request_hook_uses_hook_tool_name_without_metadata() { .lines() .map(|line| serde_json::from_str::(line).expect("parse hook input")) .collect::>(); + #[allow(deprecated)] + let turn_cwd = turn_context.cwd.clone(); assert_eq!( inputs, vec![serde_json::json!({ "session_id": session.session_id(), "turn_id": "turn_id", - "cwd": turn_context.cwd, + "cwd": turn_cwd, "transcript_path": null, "model": turn_context.model_info.slug, "permission_mode": "default", diff --git a/codex-rs/core/src/memory_usage.rs b/codex-rs/core/src/memory_usage.rs index bd856d5e6..b5bc6eef5 100644 --- a/codex-rs/core/src/memory_usage.rs +++ b/codex-rs/core/src/memory_usage.rs @@ -44,10 +44,9 @@ fn shell_command_for_invocation(invocation: &ToolInvocation) -> Option<(Vec Option<(Vec serde_json::from_str::(arguments) .ok() @@ -71,10 +69,9 @@ fn shell_command_for_invocation(invocation: &ToolInvocation) -> Option<(Vec None, } diff --git a/codex-rs/core/src/session/handlers.rs b/codex-rs/core/src/session/handlers.rs index ab40cb8ee..5cb37f982 100644 --- a/codex-rs/core/src/session/handlers.rs +++ b/codex-rs/core/src/session/handlers.rs @@ -699,6 +699,7 @@ pub async fn review( .await; sess.refresh_mcp_servers_if_requested(&turn_context, Some(sess.mcp_elicitation_reviewer())) .await; + #[allow(deprecated)] match resolve_review_request(review_request, &turn_context.cwd) { Ok(resolved) => { spawn_review_thread( diff --git a/codex-rs/core/src/session/mcp.rs b/codex-rs/core/src/session/mcp.rs index d32ecf2b1..fcaaa17c5 100644 --- a/codex-rs/core/src/session/mcp.rs +++ b/codex-rs/core/src/session/mcp.rs @@ -296,6 +296,7 @@ impl Session { .environment_manager .default_environment() .unwrap_or_else(|| self.services.environment_manager.local_environment()), + #[allow(deprecated)] turn_context.cwd.to_path_buf(), ), }; diff --git a/codex-rs/core/src/session/mod.rs b/codex-rs/core/src/session/mod.rs index 3ef946681..fd3f31771 100644 --- a/codex-rs/core/src/session/mod.rs +++ b/codex-rs/core/src/session/mod.rs @@ -2051,6 +2051,7 @@ impl Session { turn_context, call_id, args, + #[allow(deprecated)] turn_context.cwd.clone(), cancellation_token, ) @@ -2630,6 +2631,7 @@ impl Session { turn_context.approval_policy.value(), turn_context.config.approvals_reviewer, self.services.exec_policy.current().as_ref(), + #[allow(deprecated)] &turn_context.cwd, turn_context .features @@ -2763,6 +2765,7 @@ impl Session { contextual_user_sections.push( UserInstructions { text: user_instructions.to_string(), + #[allow(deprecated)] directory: turn_context.cwd.to_string_lossy().into_owned(), } .render(), diff --git a/codex-rs/core/src/session/review.rs b/codex-rs/core/src/session/review.rs index 083aac8c0..e84465508 100644 --- a/codex-rs/core/src/session/review.rs +++ b/codex-rs/core/src/session/review.rs @@ -107,6 +107,7 @@ pub(super) async fn spawn_review_thread( sess.thread_id().to_string(), parent_turn_context.thread_source, review_turn_id.clone(), + #[allow(deprecated)] parent_turn_context.cwd.clone(), &parent_turn_context.permission_profile, parent_turn_context.windows_sandbox_level, @@ -143,6 +144,7 @@ pub(super) async fn spawn_review_thread( network: parent_turn_context.network.clone(), windows_sandbox_level: parent_turn_context.windows_sandbox_level, shell_environment_policy: parent_turn_context.shell_environment_policy.clone(), + #[allow(deprecated)] cwd: parent_turn_context.cwd.clone(), final_output_json_schema: None, codex_self_exe: parent_turn_context.codex_self_exe.clone(), diff --git a/codex-rs/core/src/session/rollout_reconstruction_tests.rs b/codex-rs/core/src/session/rollout_reconstruction_tests.rs index 5cfcc3805..143b23d3a 100644 --- a/codex-rs/core/src/session/rollout_reconstruction_tests.rs +++ b/codex-rs/core/src/session/rollout_reconstruction_tests.rs @@ -60,6 +60,7 @@ async fn record_initial_history_resumed_bare_turn_context_does_not_hydrate_previ let previous_context_item = TurnContextItem { turn_id: Some(turn_context.sub_id.clone()), trace_id: turn_context.trace_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.to_path_buf(), current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), @@ -101,6 +102,7 @@ async fn record_initial_history_resumed_hydrates_previous_turn_settings_from_lif let mut previous_context_item = TurnContextItem { turn_id: Some(turn_context.sub_id.clone()), trace_id: turn_context.trace_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.to_path_buf(), current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), @@ -911,6 +913,7 @@ async fn record_initial_history_resumed_turn_context_after_compaction_reestablis let previous_context_item = TurnContextItem { turn_id: Some(turn_context.sub_id.clone()), trace_id: turn_context.trace_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.to_path_buf(), current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), @@ -989,6 +992,7 @@ async fn record_initial_history_resumed_turn_context_after_compaction_reestablis serde_json::to_value(Some(TurnContextItem { turn_id: Some(turn_context.sub_id.clone()), trace_id: turn_context.trace_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.to_path_buf(), current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), @@ -1020,6 +1024,7 @@ async fn record_initial_history_resumed_aborted_turn_without_id_clears_active_tu let previous_context_item = TurnContextItem { turn_id: Some(turn_context.sub_id.clone()), trace_id: turn_context.trace_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.to_path_buf(), current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), @@ -1135,6 +1140,7 @@ async fn record_initial_history_resumed_unmatched_abort_preserves_active_turn_fo let current_context_item = TurnContextItem { turn_id: Some(current_turn_id.clone()), trace_id: turn_context.trace_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.to_path_buf(), current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), @@ -1249,6 +1255,7 @@ async fn record_initial_history_resumed_trailing_incomplete_turn_compaction_clea let previous_context_item = TurnContextItem { turn_id: Some(turn_context.sub_id.clone()), trace_id: turn_context.trace_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.to_path_buf(), current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), @@ -1401,6 +1408,7 @@ async fn record_initial_history_resumed_replaced_incomplete_compacted_turn_clear let previous_context_item = TurnContextItem { turn_id: Some(turn_context.sub_id.clone()), trace_id: turn_context.trace_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.to_path_buf(), current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index 9ef67bcb8..577b2fa5e 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -2216,6 +2216,7 @@ async fn record_initial_history_forked_hydrates_previous_turn_settings() { let previous_context_item = TurnContextItem { turn_id: Some(turn_context.sub_id.clone()), trace_id: turn_context.trace_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.to_path_buf(), current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), @@ -3769,6 +3770,7 @@ async fn session_configuration_apply_preserves_absolute_cwd_write_root_on_cwd_up #[tokio::test] async fn session_update_settings_does_not_rewrite_sticky_environment_cwds() { let (session, turn_context) = make_session_and_context().await; + #[allow(deprecated)] let updated_cwd = turn_context.cwd.join("project"); std::fs::create_dir_all(updated_cwd.as_path()).expect("create project dir"); @@ -3788,8 +3790,12 @@ async fn session_update_settings_does_not_rewrite_sticky_environment_cwds() { let next_turn = session.new_default_turn().await; assert_eq!(session_cwd, updated_cwd); - assert_eq!(config.cwd, turn_context.cwd); - assert_eq!(next_turn.cwd, updated_cwd); + #[allow(deprecated)] + let turn_cwd = turn_context.cwd.clone(); + #[allow(deprecated)] + let next_turn_cwd = next_turn.cwd.clone(); + assert_eq!(config.cwd, turn_cwd); + assert_eq!(next_turn_cwd, updated_cwd); assert_eq!(next_turn.config.cwd, updated_cwd); } @@ -3873,7 +3879,9 @@ async fn absolute_cwd_update_with_turn_environment_is_allowed() { .await .expect("absolute cwd with explicit environments should succeed"); - assert_eq!(turn_context.cwd, absolute_cwd); + #[allow(deprecated)] + let turn_cwd = turn_context.cwd.clone(); + assert_eq!(turn_cwd, absolute_cwd); assert_eq!(turn_context.config.cwd, absolute_cwd); assert_eq!(turn_context.environments.turn_environments.len(), 1); } @@ -4674,7 +4682,9 @@ async fn request_permissions_emits_event_when_granular_policy_allows_requests() panic!("expected request_permissions event"); }; assert_eq!(request.call_id, call_id); - assert_eq!(request.cwd, Some(turn_context.cwd.clone())); + #[allow(deprecated)] + let turn_cwd = turn_context.cwd.clone(); + assert_eq!(request.cwd, Some(turn_cwd)); session .notify_request_permissions_response(&request.call_id, expected_response.clone()) @@ -5103,7 +5113,9 @@ async fn turn_environments_set_primary_environment() { &turn_environments.turn_environments[0].environment )); assert!(!turn_context.environments.turn_environments.is_empty()); - assert_eq!(turn_context.cwd.as_path(), selected_cwd.as_path()); + #[allow(deprecated)] + let turn_cwd = turn_context.cwd.clone(); + assert_eq!(turn_cwd.as_path(), selected_cwd.as_path()); assert_eq!(turn_context.config.cwd.as_path(), selected_cwd.as_path()); } @@ -5134,7 +5146,9 @@ async fn default_turn_overlays_session_cwd_onto_stored_thread_environments() { &turn_environment.environment, &turn_environments.turn_environments[0].environment )); - assert_eq!(turn_context.cwd, session_cwd); + #[allow(deprecated)] + let turn_cwd = turn_context.cwd.clone(); + assert_eq!(turn_cwd, session_cwd); assert_eq!(turn_context.config.cwd, session_cwd); } @@ -5152,7 +5166,9 @@ async fn default_turn_honors_empty_stored_thread_environments() { assert!(turn_context.environments.primary().is_none()); assert!(turn_context.environments.turn_environments.is_empty()); - assert_eq!(turn_context.cwd, session_cwd); + #[allow(deprecated)] + let turn_cwd = turn_context.cwd.clone(); + assert_eq!(turn_cwd, session_cwd); assert_eq!(turn_context.config.cwd, session_cwd); assert_eq!(turn_context.environments.turn_environments.len(), 0); } @@ -5161,6 +5177,7 @@ async fn default_turn_honors_empty_stored_thread_environments() { async fn primary_environment_uses_first_turn_environment() { let (_session, mut turn_context) = make_session_and_context().await; let first_environment = turn_context.environments.turn_environments[0].clone(); + #[allow(deprecated)] let second_cwd = turn_context.cwd.join("second"); turn_context .environments @@ -5214,7 +5231,9 @@ async fn empty_turn_environments_clear_primary_environment() { assert!(turn_context.environments.primary().is_none()); assert!(turn_context.environments.turn_environments.is_empty()); - assert_eq!(turn_context.cwd, session.get_config().await.cwd); + #[allow(deprecated)] + let turn_cwd = turn_context.cwd.clone(); + assert_eq!(turn_cwd, session.get_config().await.cwd); assert_eq!(turn_context.config.cwd, session.get_config().await.cwd); } @@ -7033,13 +7052,16 @@ async fn build_initial_context_restates_realtime_start_when_reference_context_is } fn file_system_policy_with_unreadable_glob(turn_context: &TurnContext) -> FileSystemSandboxPolicy { + #[allow(deprecated)] let mut policy = FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd( &turn_context.sandbox_policy(), &turn_context.cwd, ); + #[allow(deprecated)] + let cwd_display = turn_context.cwd.as_path().display().to_string(); policy.entries.push(FileSystemSandboxEntry { path: FileSystemPath::GlobPattern { - pattern: format!("{}/**/*.env", turn_context.cwd.as_path().display()), + pattern: format!("{cwd_display}/**/*.env"), }, access: FileSystemAccessMode::None, }); @@ -9381,6 +9403,8 @@ async fn rejects_escalated_permissions_when_policy_not_on_request() { let call_id = "test-call".to_string(); let handler = ShellCommandHandler::from(ShellCommandBackendConfig::Classic); + #[allow(deprecated)] + let workdir = Some(turn_context.cwd.to_string_lossy().to_string()); let resp = handler .handle(ToolInvocation { session: Arc::clone(&session), @@ -9393,7 +9417,7 @@ async fn rejects_escalated_permissions_when_policy_not_on_request() { payload: ToolPayload::Function { arguments: serde_json::json!({ "command": command_script, - "workdir": Some(turn_context.cwd.to_string_lossy().to_string()), + "workdir": workdir, "timeout_ms": timeout_ms, "sandbox_permissions": sandbox_permissions, "justification": Some("test"), @@ -9433,6 +9457,7 @@ async fn rejects_escalated_permissions_when_policy_not_on_request() { approval_policy: turn_context.approval_policy.value(), permission_profile: turn_context.permission_profile(), file_system_sandbox_policy: &file_system_sandbox_policy, + #[allow(deprecated)] sandbox_cwd: turn_context.cwd.as_path(), sandbox_permissions: SandboxPermissions::UseDefault, prefix_rule: None, diff --git a/codex-rs/core/src/session/tests/guardian_tests.rs b/codex-rs/core/src/session/tests/guardian_tests.rs index ffb0d94d8..718b9bf05 100644 --- a/codex-rs/core/src/session/tests/guardian_tests.rs +++ b/codex-rs/core/src/session/tests/guardian_tests.rs @@ -291,6 +291,8 @@ async fn guardian_allows_shell_command_additional_permissions_requests_past_poli let handler = crate::tools::handlers::ShellCommandHandler::from( codex_tools::ShellCommandBackendConfig::Classic, ); + #[allow(deprecated)] + let workdir = Some(turn_context.cwd.to_string_lossy().to_string()); let resp = handler .handle(ToolInvocation { session: Arc::clone(&session), @@ -304,7 +306,7 @@ async fn guardian_allows_shell_command_additional_permissions_requests_past_poli arguments: serde_json::json!({ "command": "echo hi", "login": false, - "workdir": Some(turn_context.cwd.to_string_lossy().to_string()), + "workdir": workdir, "timeout_ms": expiration_ms, "sandbox_permissions": SandboxPermissions::WithAdditionalPermissions, "additional_permissions": PermissionProfile { @@ -392,6 +394,8 @@ async fn strict_auto_review_turn_grant_forces_guardian_for_shell_command_policy_ let handler = crate::tools::handlers::ShellCommandHandler::from( codex_tools::ShellCommandBackendConfig::Classic, ); + #[allow(deprecated)] + let workdir = Some(turn_context.cwd.to_string_lossy().to_string()); let resp = handler .handle(ToolInvocation { session: Arc::clone(&session), @@ -405,7 +409,7 @@ async fn strict_auto_review_turn_grant_forces_guardian_for_shell_command_policy_ arguments: serde_json::json!({ "command": "echo hi", "login": false, - "workdir": Some(turn_context.cwd.to_string_lossy().to_string()), + "workdir": workdir, "timeout_ms": 1_000_u64, }) .to_string(), @@ -558,6 +562,8 @@ async fn shell_command_allows_sticky_turn_permissions_without_inline_request_per let handler = crate::tools::handlers::ShellCommandHandler::from( codex_tools::ShellCommandBackendConfig::Classic, ); + #[allow(deprecated)] + let workdir = Some(turn_context.cwd.to_string_lossy().to_string()); let resp = handler .handle(ToolInvocation { session: Arc::clone(&session), @@ -572,7 +578,7 @@ async fn shell_command_allows_sticky_turn_permissions_without_inline_request_per "command": "echo hi", "login": false, "timeout_ms": 1_000_u64, - "workdir": Some(turn_context.cwd.to_string_lossy().to_string()), + "workdir": workdir, }) .to_string(), }, diff --git a/codex-rs/core/src/session/turn.rs b/codex-rs/core/src/session/turn.rs index 2dd84213e..d910e03b2 100644 --- a/codex-rs/core/src/session/turn.rs +++ b/codex-rs/core/src/session/turn.rs @@ -366,6 +366,7 @@ pub(crate) async fn run_turn( let mut stop_hook_active = false; // Although from the perspective of codex.rs, TurnDiffTracker has the lifecycle of a Task which contains // many turns, from the perspective of the user, it is a single turn. + #[allow(deprecated)] let display_root = get_git_repo_root(turn_context.cwd.as_path()) .unwrap_or_else(|| turn_context.cwd.clone().into_path_buf()); let turn_diff_tracker = Arc::new(tokio::sync::Mutex::new(TurnDiffTracker::with_display_root( @@ -524,6 +525,7 @@ pub(crate) async fn run_turn( let stop_request = codex_hooks::StopRequest { session_id: sess.session_id().into(), turn_id: turn_context.sub_id.clone(), + #[allow(deprecated)] cwd: turn_context.cwd.clone(), transcript_path: sess.hook_transcript_path().await, model: turn_context.model_info.slug.clone(), @@ -573,6 +575,7 @@ pub(crate) async fn run_turn( .hooks() .dispatch(HookPayload { session_id: sess.session_id().into(), + #[allow(deprecated)] cwd: turn_context.cwd.clone(), client: turn_context.app_server_client_name.clone(), triggered_at: chrono::Utc::now(), @@ -697,6 +700,7 @@ async fn track_turn_resolved_config_analytics( model: turn_context.model_info.slug.clone(), model_provider: turn_context.config.model_provider_id.clone(), permission_profile: turn_context.permission_profile(), + #[allow(deprecated)] permission_profile_cwd: turn_context.cwd.to_path_buf(), reasoning_effort: turn_context.reasoning_effort, reasoning_summary: Some(turn_context.reasoning_summary), @@ -993,6 +997,7 @@ pub(crate) fn build_prompt( } #[allow(clippy::too_many_arguments)] +#[allow(deprecated)] #[instrument(level = "trace", skip_all, fields( diff --git a/codex-rs/core/src/session/turn_context.rs b/codex-rs/core/src/session/turn_context.rs index 7b22ddcc3..8caec7aae 100644 --- a/codex-rs/core/src/session/turn_context.rs +++ b/codex-rs/core/src/session/turn_context.rs @@ -69,6 +69,7 @@ pub struct TurnContext { /// The session's absolute working directory. All relative paths provided /// by the model as well as sandbox policies are resolved against this path /// instead of `std::env::current_dir()`. + #[deprecated(note = "use the selected turn environment cwd instead")] pub(crate) cwd: AbsolutePathBuf, pub(crate) current_date: Option, pub(crate) timezone: Option, @@ -118,6 +119,7 @@ impl TurnContext { &self.permission_profile, &file_system_sandbox_policy, network_sandbox_policy, + #[allow(deprecated)] &self.cwd, ) } @@ -253,6 +255,7 @@ impl TurnContext { session_source: self.session_source.clone(), thread_source: self.thread_source, environments: self.environments.clone(), + #[allow(deprecated)] cwd: self.cwd.clone(), current_date: self.current_date.clone(), timezone: self.timezone.clone(), @@ -288,7 +291,9 @@ impl TurnContext { } } + #[deprecated(note = "resolve paths from the selected turn environment cwd instead")] pub(crate) fn resolve_path(&self, path: Option) -> AbsolutePathBuf { + #[allow(deprecated)] path.as_ref() .map_or_else(|| self.cwd.clone(), |path| self.cwd.join(path)) } @@ -314,6 +319,7 @@ impl TurnContext { ); FileSystemSandboxContext { permissions, + #[allow(deprecated)] cwd: Some(self.cwd.clone()), windows_sandbox_level: self.windows_sandbox_level, windows_sandbox_private_desktop: self @@ -332,6 +338,7 @@ impl TurnContext { let legacy_file_system_sandbox_policy = FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd( &self.sandbox_policy(), + #[allow(deprecated)] &self.cwd, ); let file_system_sandbox_policy = self.file_system_sandbox_policy(); @@ -349,6 +356,7 @@ impl TurnContext { TurnContextItem { turn_id: Some(self.sub_id.clone()), trace_id: self.trace_id.clone(), + #[allow(deprecated)] cwd: self.cwd.to_path_buf(), current_date: self.current_date.clone(), timezone: self.timezone.clone(), @@ -566,6 +574,7 @@ impl Session { session_source, thread_source: session_configuration.thread_source, environments, + #[allow(deprecated)] cwd, current_date: Some(current_date), timezone: Some(timezone), diff --git a/codex-rs/core/src/tasks/user_shell.rs b/codex-rs/core/src/tasks/user_shell.rs index 683856b90..23f3882ed 100644 --- a/codex-rs/core/src/tasks/user_shell.rs +++ b/codex-rs/core/src/tasks/user_shell.rs @@ -148,6 +148,7 @@ pub(crate) async fn execute_user_shell_command( let exec_command = maybe_wrap_shell_lc_with_snapshot( &display_command, session_shell.as_ref(), + #[allow(deprecated)] &turn_context.cwd, &turn_context.shell_environment_policy.r#set, &exec_env_map, @@ -155,6 +156,7 @@ pub(crate) async fn execute_user_shell_command( let call_id = Uuid::new_v4().to_string(); let raw_command = command; + #[allow(deprecated)] let cwd = turn_context.cwd.clone(); let parsed_cmd = parse_command(&display_command); diff --git a/codex-rs/core/src/tools/handlers/agent_jobs/spawn_agents_on_csv.rs b/codex-rs/core/src/tools/handlers/agent_jobs/spawn_agents_on_csv.rs index 6dd7ea2b3..e1a37be6b 100644 --- a/codex-rs/core/src/tools/handlers/agent_jobs/spawn_agents_on_csv.rs +++ b/codex-rs/core/src/tools/handlers/agent_jobs/spawn_agents_on_csv.rs @@ -68,6 +68,7 @@ pub async fn handle( } let db = required_state_db(&session)?; + #[allow(deprecated)] let input_path = turn.resolve_path(Some(args.csv_path)); let input_path_display = input_path.display().to_string(); let csv_content = tokio::fs::read_to_string(&input_path) @@ -141,7 +142,10 @@ 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| turn.resolve_path(Some(path)), + |path| { + #[allow(deprecated)] + turn.resolve_path(Some(path)) + }, ); let job_suffix = &job_id[..8]; let job_name = format!("agent-job-{job_suffix}"); diff --git a/codex-rs/core/src/tools/handlers/multi_agents_common.rs b/codex-rs/core/src/tools/handlers/multi_agents_common.rs index cc4e9ebfc..216a420e3 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents_common.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents_common.rs @@ -268,7 +268,9 @@ pub(crate) fn apply_spawn_agent_runtime_overrides( })?; config.permissions.shell_environment_policy = turn.shell_environment_policy.clone(); config.codex_linux_sandbox_exe = turn.codex_linux_sandbox_exe.clone(); - config.cwd = turn.cwd.clone(); + #[allow(deprecated)] + let turn_cwd = turn.cwd.clone(); + config.cwd = turn_cwd; config .permissions .set_permission_profile(turn.permission_profile()) diff --git a/codex-rs/core/src/tools/handlers/multi_agents_tests.rs b/codex-rs/core/src/tools/handlers/multi_agents_tests.rs index f051fb0f7..1a5856173 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents_tests.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents_tests.rs @@ -2088,6 +2088,7 @@ async fn spawn_agent_reapplies_runtime_sandbox_after_role_config() { let manager = thread_manager(); session.services.agent_control = manager.agent_control(); let expected_sandbox = turn.config.legacy_sandbox_policy(); + #[allow(deprecated)] let mut expected_file_system_sandbox_policy = FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(&expected_sandbox, &turn.cwd); expected_file_system_sandbox_policy @@ -3767,15 +3768,20 @@ async fn build_agent_spawn_config_uses_turn_context_values() { ..ShellEnvironmentPolicy::default() }; let temp_dir = tempfile::tempdir().expect("temp dir"); - turn.cwd = temp_dir.abs(); + #[allow(deprecated)] + { + turn.cwd = temp_dir.abs(); + } turn.codex_linux_sandbox_exe = Some(PathBuf::from("/bin/echo")); + #[allow(deprecated)] + let turn_cwd = turn.cwd.clone(); let sandbox_policy = pick_allowed_sandbox_policy( &turn.config.permissions.permission_profile, turn.config.legacy_sandbox_policy(), - turn.cwd.as_path(), + turn_cwd.as_path(), ); let file_system_sandbox_policy = - FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(&sandbox_policy, &turn.cwd); + FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(&sandbox_policy, &turn_cwd); let network_sandbox_policy = NetworkSandboxPolicy::from(&sandbox_policy); let permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement( SandboxEnforcement::from_legacy_sandbox_policy(&sandbox_policy), @@ -3798,7 +3804,10 @@ async fn build_agent_spawn_config_uses_turn_context_values() { expected.compact_prompt = turn.compact_prompt.clone(); expected.permissions.shell_environment_policy = turn.shell_environment_policy.clone(); expected.codex_linux_sandbox_exe = turn.codex_linux_sandbox_exe.clone(); - expected.cwd = turn.cwd.clone(); + #[allow(deprecated)] + { + expected.cwd = turn.cwd.clone(); + } expected .permissions .approval_policy @@ -3849,7 +3858,10 @@ async fn build_agent_resume_config_clears_base_instructions() { expected.compact_prompt = turn.compact_prompt.clone(); expected.permissions.shell_environment_policy = turn.shell_environment_policy.clone(); expected.codex_linux_sandbox_exe = turn.codex_linux_sandbox_exe.clone(); - expected.cwd = turn.cwd.clone(); + #[allow(deprecated)] + { + expected.cwd = turn.cwd.clone(); + } expected .permissions .approval_policy diff --git a/codex-rs/core/src/tools/handlers/request_permissions.rs b/codex-rs/core/src/tools/handlers/request_permissions.rs index 243ed52d1..5d49ad861 100644 --- a/codex-rs/core/src/tools/handlers/request_permissions.rs +++ b/codex-rs/core/src/tools/handlers/request_permissions.rs @@ -47,6 +47,7 @@ impl ToolExecutor for RequestPermissionsHandler { } }; + #[allow(deprecated)] let mut args: RequestPermissionsArgs = parse_arguments_with_base_path(&arguments, &turn.cwd)?; args.permissions = normalize_additional_permissions(args.permissions.into()) diff --git a/codex-rs/core/src/tools/handlers/shell.rs b/codex-rs/core/src/tools/handlers/shell.rs index e6ce6908f..84f400854 100644 --- a/codex-rs/core/src/tools/handlers/shell.rs +++ b/codex-rs/core/src/tools/handlers/shell.rs @@ -92,6 +92,7 @@ async fn run_exec_like(args: RunExecLikeArgs) -> Result Result for ShellCommandHandler { ))); }; + #[allow(deprecated)] let cwd = resolve_workdir_base_path(&arguments, &turn.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(), diff --git a/codex-rs/core/src/tools/handlers/shell_tests.rs b/codex-rs/core/src/tools/handlers/shell_tests.rs index 9db561d57..660bac678 100644 --- a/codex-rs/core/src/tools/handlers/shell_tests.rs +++ b/codex-rs/core/src/tools/handlers/shell_tests.rs @@ -88,6 +88,7 @@ async fn shell_command_handler_to_exec_params_uses_session_shell_and_turn_contex let expected_command = session .user_shell() .derive_exec_args(&command, /*use_login_shell*/ true); + #[allow(deprecated)] let expected_cwd = turn_context.resolve_path(workdir.clone()); let expected_env = create_env( &turn_context.shell_environment_policy, diff --git a/codex-rs/core/src/tools/handlers/view_image.rs b/codex-rs/core/src/tools/handlers/view_image.rs index 3efbedc0e..bb74adb86 100644 --- a/codex-rs/core/src/tools/handlers/view_image.rs +++ b/codex-rs/core/src/tools/handlers/view_image.rs @@ -282,7 +282,10 @@ 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(); - turn.cwd = image_cwd.clone(); + #[allow(deprecated)] + { + turn.cwd = image_cwd.clone(); + } turn.environments .turn_environments .first_mut() diff --git a/codex-rs/core/src/tools/network_approval.rs b/codex-rs/core/src/tools/network_approval.rs index 14af2c9c5..39441d96f 100644 --- a/codex-rs/core/src/tools/network_approval.rs +++ b/codex-rs/core/src/tools/network_approval.rs @@ -525,6 +525,7 @@ impl NetworkApprovalService { guardian_approval_id, /*approval_id*/ None, prompt_command, + #[allow(deprecated)] turn_context.cwd.clone(), Some(prompt_reason), Some(network_approval_context.clone()), diff --git a/codex-rs/core/src/tools/orchestrator.rs b/codex-rs/core/src/tools/orchestrator.rs index 648d3f79e..deb9ae596 100644 --- a/codex-rs/core/src/tools/orchestrator.rs +++ b/codex-rs/core/src/tools/orchestrator.rs @@ -234,6 +234,7 @@ impl ToolOrchestrator { // Platform-specific flag gating is handled by SandboxManager::select_initial. let use_legacy_landlock = turn_ctx.features.use_legacy_landlock(); + #[allow(deprecated)] let sandbox_cwd = tool.sandbox_cwd(req).unwrap_or(&turn_ctx.cwd); let initial_attempt = SandboxAttempt { sandbox: initial_sandbox, diff --git a/codex-rs/core/src/tools/registry.rs b/codex-rs/core/src/tools/registry.rs index 85c346f48..6486e7db7 100644 --- a/codex-rs/core/src/tools/registry.rs +++ b/codex-rs/core/src/tools/registry.rs @@ -409,6 +409,7 @@ impl ToolRegistry { "sandbox_policy", permission_profile_policy_tag( &invocation.turn.permission_profile, + #[allow(deprecated)] invocation.turn.cwd.as_path(), ), ), diff --git a/codex-rs/core/src/unified_exec/mod_tests.rs b/codex-rs/core/src/unified_exec/mod_tests.rs index f8a1480af..6926f9cbf 100644 --- a/codex-rs/core/src/unified_exec/mod_tests.rs +++ b/codex-rs/core/src/unified_exec/mod_tests.rs @@ -83,6 +83,7 @@ async fn exec_command_with_tty( ) -> Result { let manager = &session.services.unified_exec_manager; let process_id = manager.allocate_process_id().await; + #[allow(deprecated)] let cwd = workdir .as_ref() .map_or_else(|| turn.cwd.clone(), |workdir| turn.cwd.join(workdir)); @@ -501,10 +502,12 @@ async fn reusing_completed_process_returns_unknown_process() -> anyhow::Result<( #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn completed_pipe_commands_preserve_exit_code() -> anyhow::Result<()> { let (_, turn) = make_session_and_context().await; + #[allow(deprecated)] + let cwd = turn.cwd.clone(); let request = test_exec_request( &turn, vec!["bash".to_string(), "-lc".to_string(), "exit 17".to_string()], - turn.cwd.clone(), + cwd, shell_env(), ); @@ -598,10 +601,12 @@ async fn remote_exec_server_rejects_inherited_fd_launches() -> anyhow::Result<() turn.environments.turn_environments[0].environment = Arc::new(remote_test_env.environment().clone()); + #[allow(deprecated)] + let cwd = turn.cwd.clone(); let request = test_exec_request( &turn, vec!["bash".to_string(), "-lc".to_string(), "echo ok".to_string()], - turn.cwd.clone(), + cwd, shell_env(), ); diff --git a/codex-rs/core/src/unified_exec/process_manager_tests.rs b/codex-rs/core/src/unified_exec/process_manager_tests.rs index 5ef599403..87d9553dd 100644 --- a/codex-rs/core/src/unified_exec/process_manager_tests.rs +++ b/codex-rs/core/src/unified_exec/process_manager_tests.rs @@ -175,7 +175,9 @@ async fn failed_initial_end_for_unstored_process_uses_fallback_output() { process_id: 123, yield_time_ms: 1000, max_output_tokens: None, + #[allow(deprecated)] cwd: turn.cwd.clone(), + #[allow(deprecated)] sandbox_cwd: turn.cwd.clone(), environment: turn .environments @@ -200,6 +202,7 @@ async fn failed_initial_end_for_unstored_process_uses_fallback_output() { /*process_started_alive*/ false, &context, &request, + #[allow(deprecated)] turn.cwd.clone(), transcript, "PRE_DENIAL_MARKER".to_string(),