diff --git a/.codex/skills/path-types/SKILL.md b/.codex/skills/path-types/SKILL.md index e604ce79bf1e..bc3809d3d2ec 100644 --- a/.codex/skills/path-types/SKILL.md +++ b/.codex/skills/path-types/SKILL.md @@ -9,10 +9,12 @@ Apply this guidance when defining new types. Change existing code only when expl and keep edits minimal and proportional. Treat these rules as the target state of an ongoing migration; if compliance is difficult, ask the user how to proceed. -- In app-server protocol types, use `ApiPathString` for backwards compatibility during the URI +- In app-server protocol types, use `LegacyAppPathString` for backwards compatibility during the URI migration. At the protocol boundary, convert it to `PathUri` and use `PathUri` internally. For host-local logic, such as some config values, use `AbsolutePathBuf` or `PathBuf` instead. - In exec-server protocol types, use `PathUri`. Internally, use `PathUri` or `AbsolutePathBuf` as appropriate. - In dependencies shared by both servers, use `PathUri` or separate APIs that decouple their use cases. +- Tool call arguments that the model is expected to generate should be deserialized as regular + `String`s with feature-specific path handling code. diff --git a/codex-rs/app-server-protocol/schema/json/CommandExecutionRequestApprovalParams.json b/codex-rs/app-server-protocol/schema/json/CommandExecutionRequestApprovalParams.json index c59141103cd4..323247e16c29 100644 --- a/codex-rs/app-server-protocol/schema/json/CommandExecutionRequestApprovalParams.json +++ b/codex-rs/app-server-protocol/schema/json/CommandExecutionRequestApprovalParams.json @@ -27,7 +27,7 @@ "read": { "description": "This will be removed in favor of `entries`.", "items": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": [ "array", @@ -37,7 +37,7 @@ "write": { "description": "This will be removed in favor of `entries`.", "items": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": [ "array", @@ -84,9 +84,6 @@ }, "type": "object" }, - "ApiPathString": { - "type": "string" - }, "CommandAction": { "oneOf": [ { @@ -289,7 +286,7 @@ { "properties": { "path": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ @@ -472,6 +469,9 @@ } ] }, + "LegacyAppPathString": { + "type": "string" + }, "NetworkApprovalContext": { "properties": { "host": { diff --git a/codex-rs/app-server-protocol/schema/json/PermissionsRequestApprovalParams.json b/codex-rs/app-server-protocol/schema/json/PermissionsRequestApprovalParams.json index 4d7eff3eb558..29b71c8912e3 100644 --- a/codex-rs/app-server-protocol/schema/json/PermissionsRequestApprovalParams.json +++ b/codex-rs/app-server-protocol/schema/json/PermissionsRequestApprovalParams.json @@ -27,7 +27,7 @@ "read": { "description": "This will be removed in favor of `entries`.", "items": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": [ "array", @@ -37,7 +37,7 @@ "write": { "description": "This will be removed in favor of `entries`.", "items": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": [ "array", @@ -58,9 +58,6 @@ }, "type": "object" }, - "ApiPathString": { - "type": "string" - }, "FileSystemAccessMode": { "enum": [ "read", @@ -74,7 +71,7 @@ { "properties": { "path": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ @@ -257,6 +254,9 @@ } ] }, + "LegacyAppPathString": { + "type": "string" + }, "RequestPermissionProfile": { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/PermissionsRequestApprovalResponse.json b/codex-rs/app-server-protocol/schema/json/PermissionsRequestApprovalResponse.json index e0bfd161f21a..909cb5f67021 100644 --- a/codex-rs/app-server-protocol/schema/json/PermissionsRequestApprovalResponse.json +++ b/codex-rs/app-server-protocol/schema/json/PermissionsRequestApprovalResponse.json @@ -23,7 +23,7 @@ "read": { "description": "This will be removed in favor of `entries`.", "items": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": [ "array", @@ -33,7 +33,7 @@ "write": { "description": "This will be removed in favor of `entries`.", "items": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": [ "array", @@ -54,9 +54,6 @@ }, "type": "object" }, - "ApiPathString": { - "type": "string" - }, "FileSystemAccessMode": { "enum": [ "read", @@ -70,7 +67,7 @@ { "properties": { "path": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ @@ -278,6 +275,9 @@ }, "type": "object" }, + "LegacyAppPathString": { + "type": "string" + }, "PermissionGrantScope": { "enum": [ "turn", diff --git a/codex-rs/app-server-protocol/schema/json/ServerNotification.json b/codex-rs/app-server-protocol/schema/json/ServerNotification.json index 1cf3d96c8172..d64befa0a08b 100644 --- a/codex-rs/app-server-protocol/schema/json/ServerNotification.json +++ b/codex-rs/app-server-protocol/schema/json/ServerNotification.json @@ -107,7 +107,7 @@ "read": { "description": "This will be removed in favor of `entries`.", "items": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": [ "array", @@ -117,7 +117,7 @@ "write": { "description": "This will be removed in favor of `entries`.", "items": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": [ "array", @@ -164,9 +164,6 @@ "AgentPath": { "type": "string" }, - "ApiPathString": { - "type": "string" - }, "AppBranding": { "description": "EXPERIMENTAL - app metadata returned by app-list APIs.", "properties": { @@ -1345,7 +1342,7 @@ { "properties": { "path": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ @@ -2339,6 +2336,9 @@ ], "type": "object" }, + "LegacyAppPathString": { + "type": "string" + }, "McpServerOauthLoginCompletedNotification": { "properties": { "error": { diff --git a/codex-rs/app-server-protocol/schema/json/ServerRequest.json b/codex-rs/app-server-protocol/schema/json/ServerRequest.json index 34ccaa47e9c7..70f54f35cec1 100644 --- a/codex-rs/app-server-protocol/schema/json/ServerRequest.json +++ b/codex-rs/app-server-protocol/schema/json/ServerRequest.json @@ -27,7 +27,7 @@ "read": { "description": "This will be removed in favor of `entries`.", "items": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": [ "array", @@ -37,7 +37,7 @@ "write": { "description": "This will be removed in favor of `entries`.", "items": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": [ "array", @@ -84,9 +84,6 @@ }, "type": "object" }, - "ApiPathString": { - "type": "string" - }, "ApplyPatchApprovalParams": { "properties": { "callId": { @@ -643,7 +640,7 @@ { "properties": { "path": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ @@ -826,6 +823,9 @@ } ] }, + "LegacyAppPathString": { + "type": "string" + }, "McpElicitationArrayType": { "enum": [ "array" 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 eb659fda3be9..97c4eee3cff2 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 @@ -6071,7 +6071,7 @@ "read": { "description": "This will be removed in favor of `entries`.", "items": { - "$ref": "#/definitions/v2/ApiPathString" + "$ref": "#/definitions/v2/LegacyAppPathString" }, "type": [ "array", @@ -6081,7 +6081,7 @@ "write": { "description": "This will be removed in favor of `entries`.", "items": { - "$ref": "#/definitions/v2/ApiPathString" + "$ref": "#/definitions/v2/LegacyAppPathString" }, "type": [ "array", @@ -6193,9 +6193,6 @@ }, "type": "object" }, - "ApiPathString": { - "type": "string" - }, "AppBranding": { "description": "EXPERIMENTAL - app metadata returned by app-list APIs.", "properties": { @@ -9505,7 +9502,7 @@ { "properties": { "path": { - "$ref": "#/definitions/v2/ApiPathString" + "$ref": "#/definitions/v2/LegacyAppPathString" }, "type": { "enum": [ @@ -11184,6 +11181,9 @@ "title": "ItemStartedNotification", "type": "object" }, + "LegacyAppPathString": { + "type": "string" + }, "ListMcpServerStatusParams": { "$schema": "http://json-schema.org/draft-07/schema#", "properties": { 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 e0562760f6b7..1f5e7ac2bbc9 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 @@ -273,7 +273,7 @@ "read": { "description": "This will be removed in favor of `entries`.", "items": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": [ "array", @@ -283,7 +283,7 @@ "write": { "description": "This will be removed in favor of `entries`.", "items": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": [ "array", @@ -395,9 +395,6 @@ }, "type": "object" }, - "ApiPathString": { - "type": "string" - }, "AppBranding": { "description": "EXPERIMENTAL - app metadata returned by app-list APIs.", "properties": { @@ -5798,7 +5795,7 @@ { "properties": { "path": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ @@ -7637,6 +7634,9 @@ "title": "ItemStartedNotification", "type": "object" }, + "LegacyAppPathString": { + "type": "string" + }, "ListMcpServerStatusParams": { "$schema": "http://json-schema.org/draft-07/schema#", "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ItemGuardianApprovalReviewCompletedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/ItemGuardianApprovalReviewCompletedNotification.json index 717c4c987aa8..48a3193cc735 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ItemGuardianApprovalReviewCompletedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ItemGuardianApprovalReviewCompletedNotification.json @@ -27,7 +27,7 @@ "read": { "description": "This will be removed in favor of `entries`.", "items": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": [ "array", @@ -37,7 +37,7 @@ "write": { "description": "This will be removed in favor of `entries`.", "items": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": [ "array", @@ -58,9 +58,6 @@ }, "type": "object" }, - "ApiPathString": { - "type": "string" - }, "AutoReviewDecisionSource": { "description": "[UNSTABLE] Source that produced a terminal approval auto-review decision.", "enum": [ @@ -81,7 +78,7 @@ { "properties": { "path": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ @@ -536,6 +533,9 @@ ], "type": "string" }, + "LegacyAppPathString": { + "type": "string" + }, "NetworkApprovalProtocol": { "enum": [ "http", diff --git a/codex-rs/app-server-protocol/schema/json/v2/ItemGuardianApprovalReviewStartedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/ItemGuardianApprovalReviewStartedNotification.json index 28fec1275bb4..9f534e517d2c 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ItemGuardianApprovalReviewStartedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ItemGuardianApprovalReviewStartedNotification.json @@ -27,7 +27,7 @@ "read": { "description": "This will be removed in favor of `entries`.", "items": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": [ "array", @@ -37,7 +37,7 @@ "write": { "description": "This will be removed in favor of `entries`.", "items": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": [ "array", @@ -58,9 +58,6 @@ }, "type": "object" }, - "ApiPathString": { - "type": "string" - }, "FileSystemAccessMode": { "enum": [ "read", @@ -74,7 +71,7 @@ { "properties": { "path": { - "$ref": "#/definitions/ApiPathString" + "$ref": "#/definitions/LegacyAppPathString" }, "type": { "enum": [ @@ -529,6 +526,9 @@ ], "type": "string" }, + "LegacyAppPathString": { + "type": "string" + }, "NetworkApprovalProtocol": { "enum": [ "http", diff --git a/codex-rs/app-server-protocol/schema/typescript/ApiPathString.ts b/codex-rs/app-server-protocol/schema/typescript/LegacyAppPathString.ts similarity index 96% rename from codex-rs/app-server-protocol/schema/typescript/ApiPathString.ts rename to codex-rs/app-server-protocol/schema/typescript/LegacyAppPathString.ts index ba5a3a961c04..04e465b8a29c 100644 --- a/codex-rs/app-server-protocol/schema/typescript/ApiPathString.ts +++ b/codex-rs/app-server-protocol/schema/typescript/LegacyAppPathString.ts @@ -23,4 +23,4 @@ * [`AbsolutePathBuf`]. Relative path text remains valid until an operation * such as [`Self::to_path_uri`] requires an absolute path. */ -export type ApiPathString = string; +export type LegacyAppPathString = string; diff --git a/codex-rs/app-server-protocol/schema/typescript/index.ts b/codex-rs/app-server-protocol/schema/typescript/index.ts index c3bc3ec761a1..786d4c72a851 100644 --- a/codex-rs/app-server-protocol/schema/typescript/index.ts +++ b/codex-rs/app-server-protocol/schema/typescript/index.ts @@ -4,7 +4,6 @@ export type { AbsolutePathBuf } from "./AbsolutePathBuf"; export type { AgentMessageInputContent } from "./AgentMessageInputContent"; export type { AgentPath } from "./AgentPath"; export type { AmazonBedrockCredentialSource } from "./AmazonBedrockCredentialSource"; -export type { ApiPathString } from "./ApiPathString"; export type { ApplyPatchApprovalParams } from "./ApplyPatchApprovalParams"; export type { ApplyPatchApprovalResponse } from "./ApplyPatchApprovalResponse"; export type { AuthMode } from "./AuthMode"; @@ -43,6 +42,7 @@ export type { InitializeParams } from "./InitializeParams"; export type { InitializeResponse } from "./InitializeResponse"; export type { InputModality } from "./InputModality"; export type { InternalSessionSource } from "./InternalSessionSource"; +export type { LegacyAppPathString } from "./LegacyAppPathString"; export type { LocalShellAction } from "./LocalShellAction"; export type { LocalShellExecAction } from "./LocalShellExecAction"; export type { LocalShellStatus } from "./LocalShellStatus"; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/AdditionalFileSystemPermissions.ts b/codex-rs/app-server-protocol/schema/typescript/v2/AdditionalFileSystemPermissions.ts index 68b1fb660360..f4ca94efd3be 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/AdditionalFileSystemPermissions.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/AdditionalFileSystemPermissions.ts @@ -1,15 +1,15 @@ // 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 { ApiPathString } from "../ApiPathString"; +import type { LegacyAppPathString } from "../LegacyAppPathString"; import type { FileSystemSandboxEntry } from "./FileSystemSandboxEntry"; export type AdditionalFileSystemPermissions = { /** * This will be removed in favor of `entries`. */ -read: Array | null, +read: Array | null, /** * This will be removed in favor of `entries`. */ -write: Array | null, globScanMaxDepth?: number, entries?: Array, }; +write: Array | null, globScanMaxDepth?: number, entries?: Array, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/FileSystemPath.ts b/codex-rs/app-server-protocol/schema/typescript/v2/FileSystemPath.ts index 0733c02040dd..cf391512bb34 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/FileSystemPath.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/FileSystemPath.ts @@ -1,7 +1,7 @@ // 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 { ApiPathString } from "../ApiPathString"; +import type { LegacyAppPathString } from "../LegacyAppPathString"; import type { FileSystemSpecialPath } from "./FileSystemSpecialPath"; -export type FileSystemPath = { "type": "path", path: ApiPathString, } | { "type": "glob_pattern", pattern: string, } | { "type": "special", value: FileSystemSpecialPath, }; +export type FileSystemPath = { "type": "path", path: LegacyAppPathString, } | { "type": "glob_pattern", pattern: string, } | { "type": "special", value: FileSystemSpecialPath, }; diff --git a/codex-rs/app-server-protocol/src/protocol/v2/permissions.rs b/codex-rs/app-server-protocol/src/protocol/v2/permissions.rs index cc93e3a6c980..7434fe86535b 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2/permissions.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2/permissions.rs @@ -16,7 +16,7 @@ use codex_protocol::protocol::NetworkAccess as CoreNetworkAccess; use codex_protocol::request_permissions::PermissionGrantScope as CorePermissionGrantScope; use codex_protocol::request_permissions::RequestPermissionProfile as CoreRequestPermissionProfile; use codex_utils_absolute_path::AbsolutePathBuf; -use codex_utils_path_uri::ApiPathString; +use codex_utils_path_uri::LegacyAppPathString; use codex_utils_path_uri::PathConvention; use schemars::JsonSchema; use serde::Deserialize; @@ -57,9 +57,9 @@ impl From for NetworkApprovalContext { #[ts(export_to = "v2/")] pub struct AdditionalFileSystemPermissions { /// This will be removed in favor of `entries`. - pub read: Option>, + pub read: Option>, /// This will be removed in favor of `entries`. - pub write: Option>, + pub write: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] #[ts(optional)] pub glob_scan_max_depth: Option, @@ -78,7 +78,7 @@ impl From> for AdditionalFileSystemPe if let Some(paths) = read.as_ref() { entries.extend(paths.iter().map(|path| FileSystemSandboxEntry { path: FileSystemPath::Path { - path: ApiPathString::from_abs_path(path), + path: LegacyAppPathString::from_abs_path(path), }, access: FileSystemAccessMode::Read, })); @@ -86,14 +86,24 @@ impl From> for AdditionalFileSystemPe if let Some(paths) = write.as_ref() { entries.extend(paths.iter().map(|path| FileSystemSandboxEntry { path: FileSystemPath::Path { - path: ApiPathString::from_abs_path(path), + path: LegacyAppPathString::from_abs_path(path), }, access: FileSystemAccessMode::Write, })); } Self { - read: read.map(|paths| paths.iter().map(ApiPathString::from_abs_path).collect()), - write: write.map(|paths| paths.iter().map(ApiPathString::from_abs_path).collect()), + read: read.map(|paths| { + paths + .iter() + .map(LegacyAppPathString::from_abs_path) + .collect() + }), + write: write.map(|paths| { + paths + .iter() + .map(LegacyAppPathString::from_abs_path) + .collect() + }), glob_scan_max_depth: None, entries: Some(entries), } @@ -276,7 +286,7 @@ impl From for CoreFileSystemSpecialPath { #[ts(export_to = "v2/")] // TODO(anp): Rename this type to distinguish it from the generic protocol FileSystemPath. pub enum FileSystemPath { - Path { path: ApiPathString }, + Path { path: LegacyAppPathString }, GlobPattern { pattern: String }, Special { value: FileSystemSpecialPath }, } @@ -286,7 +296,7 @@ impl From> for FileSystemPath { fn from(value: CoreFileSystemPath) -> Self { match value { CoreFileSystemPath::Path { path } => Self::Path { - path: ApiPathString::from_abs_path(&path), + path: LegacyAppPathString::from_abs_path(&path), }, CoreFileSystemPath::GlobPattern { pattern } => Self::GlobPattern { pattern }, CoreFileSystemPath::Special { value } => Self::Special { diff --git a/codex-rs/app-server-protocol/src/protocol/v2/tests.rs b/codex-rs/app-server-protocol/src/protocol/v2/tests.rs index 2567e386c839..c690871689ce 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2/tests.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2/tests.rs @@ -35,7 +35,7 @@ use codex_protocol::user_input::UserInput as CoreUserInput; use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_absolute_path::test_support::PathBufExt; use codex_utils_absolute_path::test_support::test_path_buf; -use codex_utils_path_uri::ApiPathString; +use codex_utils_path_uri::LegacyAppPathString; use pretty_assertions::assert_eq; use serde_json::Value as JsonValue; use serde_json::json; @@ -579,8 +579,8 @@ fn additional_file_system_permissions_populates_entries_for_legacy_roots() { ); let permissions = AdditionalFileSystemPermissions::from(core_permissions.clone()); - let read_only_api_path = ApiPathString::from_abs_path(&read_only_path); - let read_write_api_path = ApiPathString::from_abs_path(&read_write_path); + let read_only_api_path = LegacyAppPathString::from_abs_path(&read_only_path); + let read_write_api_path = LegacyAppPathString::from_abs_path(&read_write_path); assert_eq!( permissions, diff --git a/codex-rs/core/tests/common/test_environment.rs b/codex-rs/core/tests/common/test_environment.rs index 386e4a0eebd6..ad2a335f56c8 100644 --- a/codex-rs/core/tests/common/test_environment.rs +++ b/codex-rs/core/tests/common/test_environment.rs @@ -1,7 +1,7 @@ use std::ffi::OsStr; use anyhow::Result; -use codex_utils_path_uri::ApiPathString; +use codex_utils_path_uri::LegacyAppPathString; use codex_utils_path_uri::PathConvention; use codex_utils_path_uri::PathUri; @@ -28,7 +28,7 @@ impl TestEnvironment { } } - pub(crate) fn remote_cwd(&self, instance_id: &str) -> Result> { + pub(crate) fn remote_cwd(&self, instance_id: &str) -> Result> { let path_uri = match self { Self::Local => return Ok(None), Self::Docker { .. } => { @@ -40,7 +40,7 @@ impl TestEnvironment { PathUri::parse(&format!("file:///C:/codex-core-test-cwd-{instance_id}"))? } }; - Ok(Some(ApiPathString::from_path_uri( + Ok(Some(LegacyAppPathString::from_path_uri( &path_uri, self.path_convention(), )?)) diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index ab9fa2a79fc7..e4196224dde4 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -164,7 +164,7 @@ pub(super) use codex_terminal_detection::TerminalInfo; pub(super) use codex_terminal_detection::TerminalName; pub(super) use codex_utils_absolute_path::AbsolutePathBuf; pub(super) use codex_utils_approval_presets::builtin_approval_presets; -pub(super) use codex_utils_path_uri::ApiPathString; +pub(super) use codex_utils_path_uri::LegacyAppPathString; pub(super) use crossterm::event::KeyCode; pub(super) use crossterm::event::KeyEvent; pub(super) use crossterm::event::KeyModifiers; diff --git a/codex-rs/tui/src/chatwidget/tests/approval_requests.rs b/codex-rs/tui/src/chatwidget/tests/approval_requests.rs index 69d75ddc6722..bde795ca9536 100644 --- a/codex-rs/tui/src/chatwidget/tests/approval_requests.rs +++ b/codex-rs/tui/src/chatwidget/tests/approval_requests.rs @@ -89,8 +89,8 @@ fn app_server_exec_approval_request_preserves_permissions_context() { .expect("absolute read path"); let write_path = AbsolutePathBuf::try_from(PathBuf::from(test_path_display("/tmp/write"))) .expect("absolute write path"); - let read_api_path = ApiPathString::from_abs_path(&read_path); - let write_api_path = ApiPathString::from_abs_path(&write_path); + let read_api_path = LegacyAppPathString::from_abs_path(&read_path); + let write_api_path = LegacyAppPathString::from_abs_path(&write_path); let request = exec_approval_request_from_params( AppServerCommandExecutionRequestApprovalParams { thread_id: "thread-1".to_string(), @@ -276,8 +276,8 @@ fn app_server_request_permissions_preserves_file_system_permissions() { .expect("absolute read path"); let write_path = AbsolutePathBuf::try_from(PathBuf::from(test_path_display("/tmp/write"))) .expect("absolute write path"); - let read_api_path = ApiPathString::from_abs_path(&read_path); - let write_api_path = ApiPathString::from_abs_path(&write_path); + let read_api_path = LegacyAppPathString::from_abs_path(&read_path); + let write_api_path = LegacyAppPathString::from_abs_path(&write_path); let cwd = AbsolutePathBuf::try_from(PathBuf::from(test_path_display("/tmp"))).expect("absolute cwd"); diff --git a/codex-rs/utils/path-uri/src/api_path_string.rs b/codex-rs/utils/path-uri/src/api_path_string.rs index 0304777769e3..f9ae8690c8fd 100644 --- a/codex-rs/utils/path-uri/src/api_path_string.rs +++ b/codex-rs/utils/path-uri/src/api_path_string.rs @@ -30,9 +30,9 @@ use ts_rs::TS; #[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, TS)] #[serde(transparent)] #[ts(type = "string")] -pub struct ApiPathString(String); +pub struct LegacyAppPathString(String); -impl ApiPathString { +impl LegacyAppPathString { /// Renders an absolute path using the current host's path convention. pub fn from_abs_path(path: &AbsolutePathBuf) -> Self { Self(path.to_string_lossy().into_owned()) @@ -48,7 +48,7 @@ impl ApiPathString { pub fn from_path_uri( path: &PathUri, convention: PathConvention, - ) -> Result { + ) -> Result { if let Some(path_bytes) = path.opaque_fallback_bytes() { return render_opaque_fallback(path, &path_bytes, convention).map(Self); } @@ -61,12 +61,15 @@ impl ApiPathString { /// Parses this API string as an absolute path using the requested native /// path convention and returns its canonical path URI. - pub fn to_path_uri(&self, convention: PathConvention) -> Result { + pub fn to_path_uri( + &self, + convention: PathConvention, + ) -> Result { let path = match convention { PathConvention::Posix => parse_posix_path(&self.0), PathConvention::Windows => parse_windows_path(&self.0), }; - path.ok_or_else(|| ApiPathStringError::InvalidNativePath { + path.ok_or_else(|| LegacyAppPathStringError::InvalidNativePath { path: self.0.clone(), convention, }) @@ -102,7 +105,7 @@ impl ApiPathString { } } -impl From for ApiPathString { +impl From for LegacyAppPathString { fn from(path: AbsolutePathBuf) -> Self { Self::from_abs_path(&path) } @@ -194,7 +197,7 @@ fn render_opaque_fallback( path: &PathUri, path_bytes: &[u8], convention: PathConvention, -) -> Result { +) -> Result { let rendered = match convention { PathConvention::Posix if path_bytes.starts_with(b"/") => { Some(String::from_utf8_lossy(path_bytes).into_owned()) @@ -202,7 +205,7 @@ fn render_opaque_fallback( PathConvention::Windows => render_windows_opaque_fallback(path_bytes), PathConvention::Posix => None, }; - rendered.ok_or_else(|| ApiPathStringError::OpaqueFallback { + rendered.ok_or_else(|| LegacyAppPathStringError::OpaqueFallback { path: path.to_string(), }) } @@ -238,13 +241,13 @@ fn is_windows_separator(character: u16) -> bool { character == u16::from(b'\\') || character == u16::from(b'/') } -impl fmt::Display for ApiPathString { +impl fmt::Display for LegacyAppPathString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.0) } } -impl Serialize for ApiPathString { +impl Serialize for LegacyAppPathString { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -253,9 +256,9 @@ impl Serialize for ApiPathString { } } -impl JsonSchema for ApiPathString { +impl JsonSchema for LegacyAppPathString { fn schema_name() -> String { - "ApiPathString".to_string() + "LegacyAppPathString".to_string() } fn json_schema(generator: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema { @@ -263,7 +266,7 @@ impl JsonSchema for ApiPathString { } } -fn render_posix_path(path: &PathUri) -> Result { +fn render_posix_path(path: &PathUri) -> Result { let url = path.to_url(); // POSIX file paths do not have a UNC authority, so `file://server/share` // cannot be represented as `/share` without losing the server identity. @@ -281,7 +284,7 @@ fn render_posix_path(path: &PathUri) -> Result { Ok(rendered) } -fn render_windows_path(path: &PathUri) -> Result { +fn render_windows_path(path: &PathUri) -> Result { let url = path.to_url(); let mut segments = path_segments(&url); let mut rendered = String::new(); @@ -342,15 +345,15 @@ fn decode_native_segment(segment: &str) -> String { String::from_utf8_lossy(&bytes).into_owned() } -fn incompatible_convention(path: &PathUri, convention: PathConvention) -> ApiPathStringError { - ApiPathStringError::IncompatibleConvention { +fn incompatible_convention(path: &PathUri, convention: PathConvention) -> LegacyAppPathStringError { + LegacyAppPathStringError::IncompatibleConvention { path: path.to_string(), convention, } } #[derive(Debug, Error, PartialEq, Eq)] -pub enum ApiPathStringError { +pub enum LegacyAppPathStringError { #[error("opaque fallback path URI `{path}` cannot be recovered as a native path")] OpaqueFallback { path: String }, #[error("path URI `{path}` cannot be rendered using {convention} path syntax")] diff --git a/codex-rs/utils/path-uri/src/api_path_string_tests.rs b/codex-rs/utils/path-uri/src/api_path_string_tests.rs index 3fbdb3dec0cc..42829a783ef0 100644 --- a/codex-rs/utils/path-uri/src/api_path_string_tests.rs +++ b/codex-rs/utils/path-uri/src/api_path_string_tests.rs @@ -301,21 +301,23 @@ fn renders_native_paths_from_shared_cases() { for case in RENDER_CASES { let path = PathUri::parse(case.uri).expect("valid file URI"); let expected = match case.expected { - RenderExpectation::RoundTrip(rendered) => Ok(ApiPathString(rendered.to_string())), - RenderExpectation::RenderOnly(rendered) => Ok(ApiPathString(rendered.to_string())), + RenderExpectation::RoundTrip(rendered) => Ok(LegacyAppPathString(rendered.to_string())), + RenderExpectation::RenderOnly(rendered) => { + Ok(LegacyAppPathString(rendered.to_string())) + } RenderExpectation::Error(ExpectedError::OpaqueFallback) => { - Err(ApiPathStringError::OpaqueFallback { + Err(LegacyAppPathStringError::OpaqueFallback { path: path.to_string(), }) } RenderExpectation::Error(ExpectedError::IncompatibleConvention) => { - Err(ApiPathStringError::IncompatibleConvention { + Err(LegacyAppPathStringError::IncompatibleConvention { path: path.to_string(), convention: case.convention, }) } }; - let actual = ApiPathString::from_path_uri(&path, case.convention); + let actual = LegacyAppPathString::from_path_uri(&path, case.convention); assert_eq!(actual, expected, "rendering {case:?}"); if let Ok(rendered) = &actual { @@ -327,14 +329,15 @@ fn renders_native_paths_from_shared_cases() { } if let RenderExpectation::RoundTrip(rendered) = case.expected { - let api_path = serde_json::from_value::(serde_json::json!(rendered)) - .expect("native path should deserialize from API text"); + let api_path = + serde_json::from_value::(serde_json::json!(rendered)) + .expect("native path should deserialize from API text"); let reparsed = api_path .to_path_uri(case.convention) .expect("native path should parse using its convention"); assert_eq!(reparsed, path, "parsing {case:?}"); assert_eq!( - ApiPathString::from_path_uri(&reparsed, case.convention), + LegacyAppPathString::from_path_uri(&reparsed, case.convention), Ok(api_path), "round-tripping {case:?}" ); @@ -345,7 +348,7 @@ fn renders_native_paths_from_shared_cases() { #[test] fn relative_api_path_serializes_and_deserializes_unchanged() { for raw_path in [".", "subdir", "subdir/file.rs"] { - let path = serde_json::from_value::(serde_json::json!(raw_path)) + let path = serde_json::from_value::(serde_json::json!(raw_path)) .expect("relative API path should deserialize"); assert_eq!( @@ -358,13 +361,13 @@ fn relative_api_path_serializes_and_deserializes_unchanged() { #[test] fn relative_api_path_is_invalid_when_converted_to_a_path_uri() { let raw_path = "subdir"; - let path = serde_json::from_value::(serde_json::json!(raw_path)) + let path = serde_json::from_value::(serde_json::json!(raw_path)) .expect("relative API path should deserialize"); assert_eq!(path.infer_absolute_path_convention(), None); assert_eq!( path.to_path_uri(PathConvention::Posix), - Err(ApiPathStringError::InvalidNativePath { + Err(LegacyAppPathStringError::InvalidNativePath { path: raw_path.to_string(), convention: PathConvention::Posix, }) @@ -377,13 +380,13 @@ fn other_non_absolute_api_paths_cannot_be_converted_to_path_uris() { (r"workspace\file.rs", PathConvention::Windows), (r"C:file.rs", PathConvention::Windows), ] { - let path = serde_json::from_value::(serde_json::json!(raw_path)) + let path = serde_json::from_value::(serde_json::json!(raw_path)) .expect("API path should deserialize without validation"); assert_eq!(path.infer_absolute_path_convention(), None); assert_eq!( path.to_path_uri(convention), - Err(ApiPathStringError::InvalidNativePath { + Err(LegacyAppPathStringError::InvalidNativePath { path: raw_path.to_string(), convention, }) @@ -409,7 +412,7 @@ fn infers_absolute_path_conventions_from_api_text() { (r"C:file.rs", None), (r"\rooted-without-drive", None), ] { - let path = serde_json::from_value::(serde_json::json!(raw_path)) + let path = serde_json::from_value::(serde_json::json!(raw_path)) .expect("API path should deserialize without validation"); assert_eq!( @@ -426,7 +429,7 @@ fn foreign_absolute_syntax_deserializes_without_host_interpretation() { (r"C:\workspace\file.rs", PathConvention::Windows), ("/workspace/file.rs", PathConvention::Posix), ] { - let path = serde_json::from_value::(serde_json::json!(raw_path)) + let path = serde_json::from_value::(serde_json::json!(raw_path)) .expect("foreign API path should deserialize"); assert_eq!(path.as_str(), raw_path); @@ -444,8 +447,8 @@ fn renders_an_absolute_path_using_the_host_convention() { .expect("native path should be absolute"); assert_eq!( - ApiPathString::from(path), - ApiPathString(native_path.to_string()) + LegacyAppPathString::from(path), + LegacyAppPathString(native_path.to_string()) ); } @@ -464,19 +467,19 @@ fn renders_native_non_unicode_windows_fallback_lossily() { AbsolutePathBuf::from_absolute_path_checked(native_path).expect("absolute native path"); assert_eq!( - ApiPathString::from_abs_path(&native_path), - ApiPathString(r"C:\bad\�".to_string()) + LegacyAppPathString::from_abs_path(&native_path), + LegacyAppPathString(r"C:\bad\�".to_string()) ); let path = PathUri::from_abs_path(&native_path); assert_eq!( - ApiPathString::from_path_uri(&path, PathConvention::Windows), - Ok(ApiPathString(r"C:\bad\�".to_string())) + LegacyAppPathString::from_path_uri(&path, PathConvention::Windows), + Ok(LegacyAppPathString(r"C:\bad\�".to_string())) ); assert_eq!( - ApiPathString::from_path_uri(&path, PathConvention::Posix), - Err(ApiPathStringError::OpaqueFallback { + LegacyAppPathString::from_path_uri(&path, PathConvention::Posix), + Err(LegacyAppPathStringError::OpaqueFallback { path: path.to_string(), }) ); @@ -485,13 +488,13 @@ fn renders_native_non_unicode_windows_fallback_lossily() { #[test] fn serializes_and_deserializes_as_a_string() { let path = PathUri::parse("file:///workspace/src/lib.rs").expect("valid file URI"); - let rendered = ApiPathString::from_path_uri(&path, PathConvention::Posix) + let rendered = LegacyAppPathString::from_path_uri(&path, PathConvention::Posix) .expect("POSIX URI should render"); let json = serde_json::to_string(&rendered).expect("rendered path should serialize"); assert_eq!(json, r#""/workspace/src/lib.rs""#); assert_eq!( - serde_json::from_str::(&json) + serde_json::from_str::(&json) .expect("rendered path should deserialize from a string"), rendered ); diff --git a/codex-rs/utils/path-uri/src/lib.rs b/codex-rs/utils/path-uri/src/lib.rs index c37ad31b4528..b835530d5b44 100644 --- a/codex-rs/utils/path-uri/src/lib.rs +++ b/codex-rs/utils/path-uri/src/lib.rs @@ -19,8 +19,8 @@ use url::Url; mod api_path_string; -pub use api_path_string::ApiPathString; -pub use api_path_string::ApiPathStringError; +pub use api_path_string::LegacyAppPathString; +pub use api_path_string::LegacyAppPathStringError; pub use api_path_string::PathConvention; pub const FILE_SCHEME: &str = "file";