diff --git a/codex-rs/analytics/src/analytics_client_tests.rs b/codex-rs/analytics/src/analytics_client_tests.rs index 9f8df04b40..5510729a5a 100644 --- a/codex-rs/analytics/src/analytics_client_tests.rs +++ b/codex-rs/analytics/src/analytics_client_tests.rs @@ -2142,7 +2142,7 @@ async fn item_lifecycle_notifications_publish_command_execution_event() { CommandAction::Read { command: "cat README.md".to_string(), name: "README.md".to_string(), - path: test_path_buf("/tmp/README.md").abs(), + path: test_path_buf("/tmp/README.md").abs().into(), }, CommandAction::ListFiles { command: "ls".to_string(), diff --git a/codex-rs/app-server-protocol/schema/json/CommandExecutionRequestApprovalParams.json b/codex-rs/app-server-protocol/schema/json/CommandExecutionRequestApprovalParams.json index 91f74376fd..8a19ffa877 100644 --- a/codex-rs/app-server-protocol/schema/json/CommandExecutionRequestApprovalParams.json +++ b/codex-rs/app-server-protocol/schema/json/CommandExecutionRequestApprovalParams.json @@ -1,10 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "definitions": { - "AbsolutePathBuf": { - "description": "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.", - "type": "string" - }, "AdditionalFileSystemPermissions": { "properties": { "entries": { @@ -95,7 +91,7 @@ "type": "string" }, "path": { - "$ref": "#/definitions/AbsolutePathBuf" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ diff --git a/codex-rs/app-server-protocol/schema/json/ServerNotification.json b/codex-rs/app-server-protocol/schema/json/ServerNotification.json index 31fe2f31c7..248b7ff431 100644 --- a/codex-rs/app-server-protocol/schema/json/ServerNotification.json +++ b/codex-rs/app-server-protocol/schema/json/ServerNotification.json @@ -779,7 +779,7 @@ "type": "string" }, "path": { - "$ref": "#/definitions/AbsolutePathBuf" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ diff --git a/codex-rs/app-server-protocol/schema/json/ServerRequest.json b/codex-rs/app-server-protocol/schema/json/ServerRequest.json index c6ac80dee5..848eb55af7 100644 --- a/codex-rs/app-server-protocol/schema/json/ServerRequest.json +++ b/codex-rs/app-server-protocol/schema/json/ServerRequest.json @@ -164,7 +164,7 @@ "type": "string" }, "path": { - "$ref": "#/definitions/AbsolutePathBuf" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json index 06c826c158..e16e17808e 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json @@ -7321,7 +7321,7 @@ "type": "string" }, "path": { - "$ref": "#/definitions/v2/AbsolutePathBuf" + "$ref": "#/definitions/v2/LegacyAppPathString" }, "type": { "enum": [ diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json index b5d882644c..0ce5f25427 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json @@ -3561,7 +3561,7 @@ "type": "string" }, "path": { - "$ref": "#/definitions/AbsolutePathBuf" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ diff --git a/codex-rs/app-server-protocol/schema/json/v2/ItemCompletedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/ItemCompletedNotification.json index 8f4b533d8f..727aafcd26 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ItemCompletedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ItemCompletedNotification.json @@ -82,7 +82,7 @@ "type": "string" }, "path": { - "$ref": "#/definitions/AbsolutePathBuf" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ diff --git a/codex-rs/app-server-protocol/schema/json/v2/ItemStartedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/ItemStartedNotification.json index 4c930afde1..55b7e03feb 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ItemStartedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ItemStartedNotification.json @@ -82,7 +82,7 @@ "type": "string" }, "path": { - "$ref": "#/definitions/AbsolutePathBuf" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ diff --git a/codex-rs/app-server-protocol/schema/json/v2/ReviewStartResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ReviewStartResponse.json index 16da9f205a..43dc21a9bf 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ReviewStartResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ReviewStartResponse.json @@ -220,7 +220,7 @@ "type": "string" }, "path": { - "$ref": "#/definitions/AbsolutePathBuf" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json index 37a78533c2..a855148d73 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json @@ -301,7 +301,7 @@ "type": "string" }, "path": { - "$ref": "#/definitions/AbsolutePathBuf" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json index 67726c13a6..920517b1bd 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json @@ -223,7 +223,7 @@ "type": "string" }, "path": { - "$ref": "#/definitions/AbsolutePathBuf" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json index cdd74a735a..24f9de4a8e 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json @@ -223,7 +223,7 @@ "type": "string" }, "path": { - "$ref": "#/definitions/AbsolutePathBuf" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json index c3e388d1fd..9e2e55f02d 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json @@ -223,7 +223,7 @@ "type": "string" }, "path": { - "$ref": "#/definitions/AbsolutePathBuf" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json index ad44bf406b..299199a693 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json @@ -301,7 +301,7 @@ "type": "string" }, "path": { - "$ref": "#/definitions/AbsolutePathBuf" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadRollbackResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadRollbackResponse.json index 5ae94d8d11..228795d898 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadRollbackResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadRollbackResponse.json @@ -223,7 +223,7 @@ "type": "string" }, "path": { - "$ref": "#/definitions/AbsolutePathBuf" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json index df70c08c43..27194bb625 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json @@ -301,7 +301,7 @@ "type": "string" }, "path": { - "$ref": "#/definitions/AbsolutePathBuf" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json index 157f6193b4..9e53836d36 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json @@ -223,7 +223,7 @@ "type": "string" }, "path": { - "$ref": "#/definitions/AbsolutePathBuf" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json index 265578dcdd..657c596edb 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json @@ -223,7 +223,7 @@ "type": "string" }, "path": { - "$ref": "#/definitions/AbsolutePathBuf" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ diff --git a/codex-rs/app-server-protocol/schema/json/v2/TurnCompletedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/TurnCompletedNotification.json index ae1b07cfc9..71b33184c3 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/TurnCompletedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnCompletedNotification.json @@ -220,7 +220,7 @@ "type": "string" }, "path": { - "$ref": "#/definitions/AbsolutePathBuf" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ diff --git a/codex-rs/app-server-protocol/schema/json/v2/TurnStartResponse.json b/codex-rs/app-server-protocol/schema/json/v2/TurnStartResponse.json index 5853612dd9..3dde065260 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/TurnStartResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnStartResponse.json @@ -220,7 +220,7 @@ "type": "string" }, "path": { - "$ref": "#/definitions/AbsolutePathBuf" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ diff --git a/codex-rs/app-server-protocol/schema/json/v2/TurnStartedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/TurnStartedNotification.json index 7db58289ee..f19a65445b 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/TurnStartedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnStartedNotification.json @@ -220,7 +220,7 @@ "type": "string" }, "path": { - "$ref": "#/definitions/AbsolutePathBuf" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/CommandAction.ts b/codex-rs/app-server-protocol/schema/typescript/v2/CommandAction.ts index a17fb06a0c..91c92c9cbf 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/CommandAction.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/CommandAction.ts @@ -1,6 +1,6 @@ // GENERATED CODE! DO NOT MODIFY BY HAND! // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { LegacyAppPathString } from "../LegacyAppPathString"; -export type CommandAction = { "type": "read", command: string, name: string, path: AbsolutePathBuf, } | { "type": "listFiles", command: string, path: string | null, } | { "type": "search", command: string, query: string | null, path: string | null, } | { "type": "unknown", command: string, }; +export type CommandAction = { "type": "read", command: string, name: string, path: LegacyAppPathString, } | { "type": "listFiles", command: string, path: string | null, } | { "type": "search", command: string, query: string | null, path: string | null, } | { "type": "unknown", command: string, }; diff --git a/codex-rs/app-server-protocol/src/protocol/item_builders.rs b/codex-rs/app-server-protocol/src/protocol/item_builders.rs index 66019980a3..43656ad8fc 100644 --- a/codex-rs/app-server-protocol/src/protocol/item_builders.rs +++ b/codex-rs/app-server-protocol/src/protocol/item_builders.rs @@ -35,7 +35,6 @@ use codex_protocol::protocol::PatchApplyBeginEvent; use codex_protocol::protocol::PatchApplyEndEvent; use codex_shell_command::parse_command::parse_command; use codex_shell_command::parse_command::shlex_join; -use codex_utils_path_uri::PathConvention; use codex_utils_path_uri::PathUri; use std::collections::HashMap; use std::path::PathBuf; @@ -129,31 +128,28 @@ pub fn build_command_execution_end_item(payload: &ExecCommandEndEvent) -> Thread } fn command_actions_for_path_uri(parsed_cmd: &[ParsedCommand], cwd: &PathUri) -> Vec { - // TODO(anp): Carry PathUri into CommandAction so foreign Read actions retain resolved paths. - // Until then, omit those actions rather than project a foreign cwd onto the host. - let native_cwd = if cwd.infer_path_convention() == Some(PathConvention::native()) { - cwd.to_abs_path().ok() - } else { - None - }; - parsed_cmd .iter() .cloned() .filter_map(|parsed| match parsed { - ParsedCommand::Read { cmd, name, path } => match native_cwd.as_ref() { - Some(native_cwd) => Some(CommandAction::Read { - command: cmd, - name, - path: native_cwd.join(path), - }), - None => { - warn!( - command = cmd, - %cwd, - "omitting read command action whose path cannot be resolved against a foreign cwd" - ); - None + ParsedCommand::Read { cmd, name, path } => { + let path = path.to_string_lossy(); + match cwd.join(&path) { + Ok(path) => Some(CommandAction::Read { + command: cmd, + name, + path: path.into(), + }), + Err(error) => { + warn!( + command = cmd, + path = %path, + %cwd, + %error, + "omitting read command action whose path cannot be resolved against its cwd" + ); + None + } } }, ParsedCommand::ListFiles { cmd, path } => { diff --git a/codex-rs/app-server-protocol/src/protocol/item_builders_tests.rs b/codex-rs/app-server-protocol/src/protocol/item_builders_tests.rs index b892fcf505..545e1cf8c2 100644 --- a/codex-rs/app-server-protocol/src/protocol/item_builders_tests.rs +++ b/codex-rs/app-server-protocol/src/protocol/item_builders_tests.rs @@ -2,7 +2,7 @@ use super::*; use pretty_assertions::assert_eq; #[test] -fn foreign_read_is_omitted_without_dropping_other_command_actions() { +fn foreign_read_retains_target_native_path_without_dropping_other_command_actions() { #[cfg(windows)] let cwd = PathUri::parse("file:///usr/local/src").expect("valid foreign POSIX cwd"); #[cfg(not(windows))] @@ -23,10 +23,26 @@ fn foreign_read_is_omitted_without_dropping_other_command_actions() { path: Some("src".to_string()), }, ]; + let read_path = cwd + .join("file.txt") + .expect("relative read path should resolve against foreign cwd"); + #[cfg(windows)] + let expected_native_path = "/usr/local/src/file.txt"; + #[cfg(not(windows))] + let expected_native_path = r"C:\src\file.txt"; + assert_eq!( + read_path.inferred_native_path_string(), + expected_native_path + ); assert_eq!( command_actions_for_path_uri(&parsed_cmd, &cwd), vec![ + CommandAction::Read { + command: "cat file.txt".to_string(), + name: "file.txt".to_string(), + path: read_path.into(), + }, CommandAction::ListFiles { command: "ls".to_string(), path: Some("subdir".to_string()), diff --git a/codex-rs/app-server-protocol/src/protocol/v2/item.rs b/codex-rs/app-server-protocol/src/protocol/v2/item.rs index e2a0d45129..f17c911d0a 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2/item.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2/item.rs @@ -110,7 +110,7 @@ pub enum CommandAction { Read { command: String, name: String, - path: AbsolutePathBuf, + path: LegacyAppPathString, }, ListFiles { command: String, @@ -174,7 +174,7 @@ impl CommandAction { } => CoreParsedCommand::Read { cmd, name, - path: path.into_path_buf(), + path: PathBuf::from(path.into_string()), }, CommandAction::ListFiles { command: cmd, path } => { CoreParsedCommand::ListFiles { cmd, path } @@ -193,7 +193,7 @@ impl CommandAction { CoreParsedCommand::Read { cmd, name, path } => CommandAction::Read { command: cmd, name, - path: cwd.join(path), + path: cwd.join(path).into(), }, CoreParsedCommand::ListFiles { cmd, path } => { CommandAction::ListFiles { command: cmd, path } diff --git a/codex-rs/app-server/README.md b/codex-rs/app-server/README.md index 88d676b5fa..d794bd52c2 100644 --- a/codex-rs/app-server/README.md +++ b/codex-rs/app-server/README.md @@ -1410,6 +1410,7 @@ There are additional item-specific events: - `item/commandExecution/outputDelta` — streams stdout/stderr for the command; append deltas in order to render live output alongside `aggregatedOutput` in the final item. Final `commandExecution` items include parsed `commandActions`, `status`, `exitCode`, and `durationMs` so the UI can summarize what ran and whether it succeeded. +- Resolved read paths in `commandActions` use the selected execution environment's native path syntax. #### fileChange