diff --git a/codex-rs/analytics/src/analytics_client_tests.rs b/codex-rs/analytics/src/analytics_client_tests.rs index bf97da4284..15f456ef3f 100644 --- a/codex-rs/analytics/src/analytics_client_tests.rs +++ b/codex-rs/analytics/src/analytics_client_tests.rs @@ -65,6 +65,7 @@ use codex_app_server_protocol::InitializeCapabilities; use codex_app_server_protocol::InitializeParams; use codex_app_server_protocol::JSONRPCErrorError; use codex_app_server_protocol::NonSteerableTurnKind; +use codex_app_server_protocol::PermissionProfile as AppServerPermissionProfile; use codex_app_server_protocol::RequestId; use codex_app_server_protocol::SandboxPolicy as AppServerSandboxPolicy; use codex_app_server_protocol::ServerNotification; @@ -91,6 +92,7 @@ use codex_plugin::PluginTelemetryMetadata; use codex_protocol::approvals::NetworkApprovalProtocol; use codex_protocol::config_types::ApprovalsReviewer; use codex_protocol::config_types::ModeKind; +use codex_protocol::models::PermissionProfile as CorePermissionProfile; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::HookEventName; use codex_protocol::protocol::HookRunStatus; @@ -152,11 +154,20 @@ fn sample_thread_start_response(thread_id: &str, ephemeral: bool, model: &str) - approval_policy: AppServerAskForApproval::OnFailure, approvals_reviewer: AppServerApprovalsReviewer::User, sandbox: AppServerSandboxPolicy::DangerFullAccess, + permission_profile: sample_permission_profile(), reasoning_effort: None, }, } } +fn sample_permission_profile() -> AppServerPermissionProfile { + CorePermissionProfile::from_legacy_sandbox_policy( + &SandboxPolicy::DangerFullAccess, + &test_path_buf("/tmp"), + ) + .into() +} + fn sample_app_server_client_metadata() -> CodexAppServerClientMetadata { CodexAppServerClientMetadata { product_client_id: DEFAULT_ORIGINATOR.to_string(), @@ -203,6 +214,7 @@ fn sample_thread_resume_response_with_source( approval_policy: AppServerAskForApproval::OnFailure, approvals_reviewer: AppServerApprovalsReviewer::User, sandbox: AppServerSandboxPolicy::DangerFullAccess, + permission_profile: sample_permission_profile(), reasoning_effort: None, }, } 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 d1518ce28f..f3dc8f25f1 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 @@ -10889,6 +10889,53 @@ } ] }, + "PermissionProfile": { + "properties": { + "fileSystem": { + "anyOf": [ + { + "$ref": "#/definitions/v2/PermissionProfileFileSystemPermissions" + }, + { + "type": "null" + } + ] + }, + "network": { + "anyOf": [ + { + "$ref": "#/definitions/v2/AdditionalNetworkPermissions" + }, + { + "type": "null" + } + ] + } + }, + "type": "object" + }, + "PermissionProfileFileSystemPermissions": { + "properties": { + "entries": { + "items": { + "$ref": "#/definitions/v2/FileSystemSandboxEntry" + }, + "type": "array" + }, + "globScanMaxDepth": { + "format": "uint", + "minimum": 1.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "entries" + ], + "type": "object" + }, "Personality": { "enum": [ "none", @@ -14048,6 +14095,9 @@ "modelProvider": { "type": "string" }, + "permissionProfile": { + "$ref": "#/definitions/v2/PermissionProfile" + }, "reasoningEffort": { "anyOf": [ { @@ -14081,6 +14131,7 @@ "cwd", "model", "modelProvider", + "permissionProfile", "sandbox", "thread" ], @@ -15438,6 +15489,9 @@ "modelProvider": { "type": "string" }, + "permissionProfile": { + "$ref": "#/definitions/v2/PermissionProfile" + }, "reasoningEffort": { "anyOf": [ { @@ -15471,6 +15525,7 @@ "cwd", "model", "modelProvider", + "permissionProfile", "sandbox", "thread" ], @@ -15737,6 +15792,9 @@ "modelProvider": { "type": "string" }, + "permissionProfile": { + "$ref": "#/definitions/v2/PermissionProfile" + }, "reasoningEffort": { "anyOf": [ { @@ -15770,6 +15828,7 @@ "cwd", "model", "modelProvider", + "permissionProfile", "sandbox", "thread" ], 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 82c990533a..ab07d7700d 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 @@ -7651,6 +7651,53 @@ } ] }, + "PermissionProfile": { + "properties": { + "fileSystem": { + "anyOf": [ + { + "$ref": "#/definitions/PermissionProfileFileSystemPermissions" + }, + { + "type": "null" + } + ] + }, + "network": { + "anyOf": [ + { + "$ref": "#/definitions/AdditionalNetworkPermissions" + }, + { + "type": "null" + } + ] + } + }, + "type": "object" + }, + "PermissionProfileFileSystemPermissions": { + "properties": { + "entries": { + "items": { + "$ref": "#/definitions/FileSystemSandboxEntry" + }, + "type": "array" + }, + "globScanMaxDepth": { + "format": "uint", + "minimum": 1.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "entries" + ], + "type": "object" + }, "Personality": { "enum": [ "none", @@ -11942,6 +11989,9 @@ "modelProvider": { "type": "string" }, + "permissionProfile": { + "$ref": "#/definitions/PermissionProfile" + }, "reasoningEffort": { "anyOf": [ { @@ -11975,6 +12025,7 @@ "cwd", "model", "modelProvider", + "permissionProfile", "sandbox", "thread" ], @@ -13332,6 +13383,9 @@ "modelProvider": { "type": "string" }, + "permissionProfile": { + "$ref": "#/definitions/PermissionProfile" + }, "reasoningEffort": { "anyOf": [ { @@ -13365,6 +13419,7 @@ "cwd", "model", "modelProvider", + "permissionProfile", "sandbox", "thread" ], @@ -13631,6 +13686,9 @@ "modelProvider": { "type": "string" }, + "permissionProfile": { + "$ref": "#/definitions/PermissionProfile" + }, "reasoningEffort": { "anyOf": [ { @@ -13664,6 +13722,7 @@ "cwd", "model", "modelProvider", + "permissionProfile", "sandbox", "thread" ], 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 a01f7bee71..b79f7ffea8 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json @@ -5,6 +5,17 @@ "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" }, + "AdditionalNetworkPermissions": { + "properties": { + "enabled": { + "type": [ + "boolean", + "null" + ] + } + }, + "type": "object" + }, "AgentPath": { "type": "string" }, @@ -448,6 +459,217 @@ ], "type": "string" }, + "FileSystemAccessMode": { + "enum": [ + "read", + "write", + "none" + ], + "type": "string" + }, + "FileSystemPath": { + "oneOf": [ + { + "properties": { + "path": { + "$ref": "#/definitions/AbsolutePathBuf" + }, + "type": { + "enum": [ + "path" + ], + "title": "PathFileSystemPathType", + "type": "string" + } + }, + "required": [ + "path", + "type" + ], + "title": "PathFileSystemPath", + "type": "object" + }, + { + "properties": { + "pattern": { + "type": "string" + }, + "type": { + "enum": [ + "glob_pattern" + ], + "title": "GlobPatternFileSystemPathType", + "type": "string" + } + }, + "required": [ + "pattern", + "type" + ], + "title": "GlobPatternFileSystemPath", + "type": "object" + }, + { + "properties": { + "type": { + "enum": [ + "special" + ], + "title": "SpecialFileSystemPathType", + "type": "string" + }, + "value": { + "$ref": "#/definitions/FileSystemSpecialPath" + } + }, + "required": [ + "type", + "value" + ], + "title": "SpecialFileSystemPath", + "type": "object" + } + ] + }, + "FileSystemSandboxEntry": { + "properties": { + "access": { + "$ref": "#/definitions/FileSystemAccessMode" + }, + "path": { + "$ref": "#/definitions/FileSystemPath" + } + }, + "required": [ + "access", + "path" + ], + "type": "object" + }, + "FileSystemSpecialPath": { + "oneOf": [ + { + "properties": { + "kind": { + "enum": [ + "root" + ], + "type": "string" + } + }, + "required": [ + "kind" + ], + "title": "RootFileSystemSpecialPath", + "type": "object" + }, + { + "properties": { + "kind": { + "enum": [ + "minimal" + ], + "type": "string" + } + }, + "required": [ + "kind" + ], + "title": "MinimalFileSystemSpecialPath", + "type": "object" + }, + { + "properties": { + "kind": { + "enum": [ + "current_working_directory" + ], + "type": "string" + } + }, + "required": [ + "kind" + ], + "title": "CurrentWorkingDirectoryFileSystemSpecialPath", + "type": "object" + }, + { + "properties": { + "kind": { + "enum": [ + "project_roots" + ], + "type": "string" + }, + "subpath": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "kind" + ], + "title": "KindFileSystemSpecialPath", + "type": "object" + }, + { + "properties": { + "kind": { + "enum": [ + "tmpdir" + ], + "type": "string" + } + }, + "required": [ + "kind" + ], + "title": "TmpdirFileSystemSpecialPath", + "type": "object" + }, + { + "properties": { + "kind": { + "enum": [ + "slash_tmp" + ], + "type": "string" + } + }, + "required": [ + "kind" + ], + "title": "SlashTmpFileSystemSpecialPath", + "type": "object" + }, + { + "properties": { + "kind": { + "enum": [ + "unknown" + ], + "type": "string" + }, + "path": { + "type": "string" + }, + "subpath": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "kind", + "path" + ], + "type": "object" + } + ] + }, "FileUpdateChange": { "properties": { "diff": { @@ -686,6 +908,53 @@ } ] }, + "PermissionProfile": { + "properties": { + "fileSystem": { + "anyOf": [ + { + "$ref": "#/definitions/PermissionProfileFileSystemPermissions" + }, + { + "type": "null" + } + ] + }, + "network": { + "anyOf": [ + { + "$ref": "#/definitions/AdditionalNetworkPermissions" + }, + { + "type": "null" + } + ] + } + }, + "type": "object" + }, + "PermissionProfileFileSystemPermissions": { + "properties": { + "entries": { + "items": { + "$ref": "#/definitions/FileSystemSandboxEntry" + }, + "type": "array" + }, + "globScanMaxDepth": { + "format": "uint", + "minimum": 1.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "entries" + ], + "type": "object" + }, "ReadOnlyAccess": { "oneOf": [ { @@ -2225,6 +2494,9 @@ "modelProvider": { "type": "string" }, + "permissionProfile": { + "$ref": "#/definitions/PermissionProfile" + }, "reasoningEffort": { "anyOf": [ { @@ -2258,6 +2530,7 @@ "cwd", "model", "modelProvider", + "permissionProfile", "sandbox", "thread" ], 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 6d504c6004..e274c10dec 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json @@ -5,6 +5,17 @@ "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" }, + "AdditionalNetworkPermissions": { + "properties": { + "enabled": { + "type": [ + "boolean", + "null" + ] + } + }, + "type": "object" + }, "AgentPath": { "type": "string" }, @@ -448,6 +459,217 @@ ], "type": "string" }, + "FileSystemAccessMode": { + "enum": [ + "read", + "write", + "none" + ], + "type": "string" + }, + "FileSystemPath": { + "oneOf": [ + { + "properties": { + "path": { + "$ref": "#/definitions/AbsolutePathBuf" + }, + "type": { + "enum": [ + "path" + ], + "title": "PathFileSystemPathType", + "type": "string" + } + }, + "required": [ + "path", + "type" + ], + "title": "PathFileSystemPath", + "type": "object" + }, + { + "properties": { + "pattern": { + "type": "string" + }, + "type": { + "enum": [ + "glob_pattern" + ], + "title": "GlobPatternFileSystemPathType", + "type": "string" + } + }, + "required": [ + "pattern", + "type" + ], + "title": "GlobPatternFileSystemPath", + "type": "object" + }, + { + "properties": { + "type": { + "enum": [ + "special" + ], + "title": "SpecialFileSystemPathType", + "type": "string" + }, + "value": { + "$ref": "#/definitions/FileSystemSpecialPath" + } + }, + "required": [ + "type", + "value" + ], + "title": "SpecialFileSystemPath", + "type": "object" + } + ] + }, + "FileSystemSandboxEntry": { + "properties": { + "access": { + "$ref": "#/definitions/FileSystemAccessMode" + }, + "path": { + "$ref": "#/definitions/FileSystemPath" + } + }, + "required": [ + "access", + "path" + ], + "type": "object" + }, + "FileSystemSpecialPath": { + "oneOf": [ + { + "properties": { + "kind": { + "enum": [ + "root" + ], + "type": "string" + } + }, + "required": [ + "kind" + ], + "title": "RootFileSystemSpecialPath", + "type": "object" + }, + { + "properties": { + "kind": { + "enum": [ + "minimal" + ], + "type": "string" + } + }, + "required": [ + "kind" + ], + "title": "MinimalFileSystemSpecialPath", + "type": "object" + }, + { + "properties": { + "kind": { + "enum": [ + "current_working_directory" + ], + "type": "string" + } + }, + "required": [ + "kind" + ], + "title": "CurrentWorkingDirectoryFileSystemSpecialPath", + "type": "object" + }, + { + "properties": { + "kind": { + "enum": [ + "project_roots" + ], + "type": "string" + }, + "subpath": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "kind" + ], + "title": "KindFileSystemSpecialPath", + "type": "object" + }, + { + "properties": { + "kind": { + "enum": [ + "tmpdir" + ], + "type": "string" + } + }, + "required": [ + "kind" + ], + "title": "TmpdirFileSystemSpecialPath", + "type": "object" + }, + { + "properties": { + "kind": { + "enum": [ + "slash_tmp" + ], + "type": "string" + } + }, + "required": [ + "kind" + ], + "title": "SlashTmpFileSystemSpecialPath", + "type": "object" + }, + { + "properties": { + "kind": { + "enum": [ + "unknown" + ], + "type": "string" + }, + "path": { + "type": "string" + }, + "subpath": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "kind", + "path" + ], + "type": "object" + } + ] + }, "FileUpdateChange": { "properties": { "diff": { @@ -686,6 +908,53 @@ } ] }, + "PermissionProfile": { + "properties": { + "fileSystem": { + "anyOf": [ + { + "$ref": "#/definitions/PermissionProfileFileSystemPermissions" + }, + { + "type": "null" + } + ] + }, + "network": { + "anyOf": [ + { + "$ref": "#/definitions/AdditionalNetworkPermissions" + }, + { + "type": "null" + } + ] + } + }, + "type": "object" + }, + "PermissionProfileFileSystemPermissions": { + "properties": { + "entries": { + "items": { + "$ref": "#/definitions/FileSystemSandboxEntry" + }, + "type": "array" + }, + "globScanMaxDepth": { + "format": "uint", + "minimum": 1.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "entries" + ], + "type": "object" + }, "ReadOnlyAccess": { "oneOf": [ { @@ -2225,6 +2494,9 @@ "modelProvider": { "type": "string" }, + "permissionProfile": { + "$ref": "#/definitions/PermissionProfile" + }, "reasoningEffort": { "anyOf": [ { @@ -2258,6 +2530,7 @@ "cwd", "model", "modelProvider", + "permissionProfile", "sandbox", "thread" ], 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 0d0ff5d9dc..7518dea3da 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json @@ -5,6 +5,17 @@ "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" }, + "AdditionalNetworkPermissions": { + "properties": { + "enabled": { + "type": [ + "boolean", + "null" + ] + } + }, + "type": "object" + }, "AgentPath": { "type": "string" }, @@ -448,6 +459,217 @@ ], "type": "string" }, + "FileSystemAccessMode": { + "enum": [ + "read", + "write", + "none" + ], + "type": "string" + }, + "FileSystemPath": { + "oneOf": [ + { + "properties": { + "path": { + "$ref": "#/definitions/AbsolutePathBuf" + }, + "type": { + "enum": [ + "path" + ], + "title": "PathFileSystemPathType", + "type": "string" + } + }, + "required": [ + "path", + "type" + ], + "title": "PathFileSystemPath", + "type": "object" + }, + { + "properties": { + "pattern": { + "type": "string" + }, + "type": { + "enum": [ + "glob_pattern" + ], + "title": "GlobPatternFileSystemPathType", + "type": "string" + } + }, + "required": [ + "pattern", + "type" + ], + "title": "GlobPatternFileSystemPath", + "type": "object" + }, + { + "properties": { + "type": { + "enum": [ + "special" + ], + "title": "SpecialFileSystemPathType", + "type": "string" + }, + "value": { + "$ref": "#/definitions/FileSystemSpecialPath" + } + }, + "required": [ + "type", + "value" + ], + "title": "SpecialFileSystemPath", + "type": "object" + } + ] + }, + "FileSystemSandboxEntry": { + "properties": { + "access": { + "$ref": "#/definitions/FileSystemAccessMode" + }, + "path": { + "$ref": "#/definitions/FileSystemPath" + } + }, + "required": [ + "access", + "path" + ], + "type": "object" + }, + "FileSystemSpecialPath": { + "oneOf": [ + { + "properties": { + "kind": { + "enum": [ + "root" + ], + "type": "string" + } + }, + "required": [ + "kind" + ], + "title": "RootFileSystemSpecialPath", + "type": "object" + }, + { + "properties": { + "kind": { + "enum": [ + "minimal" + ], + "type": "string" + } + }, + "required": [ + "kind" + ], + "title": "MinimalFileSystemSpecialPath", + "type": "object" + }, + { + "properties": { + "kind": { + "enum": [ + "current_working_directory" + ], + "type": "string" + } + }, + "required": [ + "kind" + ], + "title": "CurrentWorkingDirectoryFileSystemSpecialPath", + "type": "object" + }, + { + "properties": { + "kind": { + "enum": [ + "project_roots" + ], + "type": "string" + }, + "subpath": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "kind" + ], + "title": "KindFileSystemSpecialPath", + "type": "object" + }, + { + "properties": { + "kind": { + "enum": [ + "tmpdir" + ], + "type": "string" + } + }, + "required": [ + "kind" + ], + "title": "TmpdirFileSystemSpecialPath", + "type": "object" + }, + { + "properties": { + "kind": { + "enum": [ + "slash_tmp" + ], + "type": "string" + } + }, + "required": [ + "kind" + ], + "title": "SlashTmpFileSystemSpecialPath", + "type": "object" + }, + { + "properties": { + "kind": { + "enum": [ + "unknown" + ], + "type": "string" + }, + "path": { + "type": "string" + }, + "subpath": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "kind", + "path" + ], + "type": "object" + } + ] + }, "FileUpdateChange": { "properties": { "diff": { @@ -686,6 +908,53 @@ } ] }, + "PermissionProfile": { + "properties": { + "fileSystem": { + "anyOf": [ + { + "$ref": "#/definitions/PermissionProfileFileSystemPermissions" + }, + { + "type": "null" + } + ] + }, + "network": { + "anyOf": [ + { + "$ref": "#/definitions/AdditionalNetworkPermissions" + }, + { + "type": "null" + } + ] + } + }, + "type": "object" + }, + "PermissionProfileFileSystemPermissions": { + "properties": { + "entries": { + "items": { + "$ref": "#/definitions/FileSystemSandboxEntry" + }, + "type": "array" + }, + "globScanMaxDepth": { + "format": "uint", + "minimum": 1.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "entries" + ], + "type": "object" + }, "ReadOnlyAccess": { "oneOf": [ { @@ -2225,6 +2494,9 @@ "modelProvider": { "type": "string" }, + "permissionProfile": { + "$ref": "#/definitions/PermissionProfile" + }, "reasoningEffort": { "anyOf": [ { @@ -2258,6 +2530,7 @@ "cwd", "model", "modelProvider", + "permissionProfile", "sandbox", "thread" ], diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/PermissionProfile.ts b/codex-rs/app-server-protocol/schema/typescript/v2/PermissionProfile.ts new file mode 100644 index 0000000000..603f607720 --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/PermissionProfile.ts @@ -0,0 +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 { AdditionalNetworkPermissions } from "./AdditionalNetworkPermissions"; +import type { PermissionProfileFileSystemPermissions } from "./PermissionProfileFileSystemPermissions"; + +export type PermissionProfile = { network: AdditionalNetworkPermissions | null, fileSystem: PermissionProfileFileSystemPermissions | null, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/PermissionProfileFileSystemPermissions.ts b/codex-rs/app-server-protocol/schema/typescript/v2/PermissionProfileFileSystemPermissions.ts new file mode 100644 index 0000000000..204a42764c --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/PermissionProfileFileSystemPermissions.ts @@ -0,0 +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 { FileSystemSandboxEntry } from "./FileSystemSandboxEntry"; + +export type PermissionProfileFileSystemPermissions = { entries: Array, globScanMaxDepth?: number, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadForkResponse.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadForkResponse.ts index 470e98c9b8..add13b6f28 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadForkResponse.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadForkResponse.ts @@ -6,6 +6,7 @@ import type { ReasoningEffort } from "../ReasoningEffort"; import type { ServiceTier } from "../ServiceTier"; import type { ApprovalsReviewer } from "./ApprovalsReviewer"; import type { AskForApproval } from "./AskForApproval"; +import type { PermissionProfile } from "./PermissionProfile"; import type { SandboxPolicy } from "./SandboxPolicy"; import type { Thread } from "./Thread"; @@ -17,4 +18,4 @@ instructionSources: Array, approvalPolicy: AskForApproval, /** * Reviewer currently used for approval requests on this thread. */ -approvalsReviewer: ApprovalsReviewer, sandbox: SandboxPolicy, reasoningEffort: ReasoningEffort | null, }; +approvalsReviewer: ApprovalsReviewer, sandbox: SandboxPolicy, permissionProfile: PermissionProfile, reasoningEffort: ReasoningEffort | null, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadResumeResponse.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadResumeResponse.ts index 177add8350..cf73f3997c 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadResumeResponse.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadResumeResponse.ts @@ -6,6 +6,7 @@ import type { ReasoningEffort } from "../ReasoningEffort"; import type { ServiceTier } from "../ServiceTier"; import type { ApprovalsReviewer } from "./ApprovalsReviewer"; import type { AskForApproval } from "./AskForApproval"; +import type { PermissionProfile } from "./PermissionProfile"; import type { SandboxPolicy } from "./SandboxPolicy"; import type { Thread } from "./Thread"; @@ -17,4 +18,4 @@ instructionSources: Array, approvalPolicy: AskForApproval, /** * Reviewer currently used for approval requests on this thread. */ -approvalsReviewer: ApprovalsReviewer, sandbox: SandboxPolicy, reasoningEffort: ReasoningEffort | null, }; +approvalsReviewer: ApprovalsReviewer, sandbox: SandboxPolicy, permissionProfile: PermissionProfile, reasoningEffort: ReasoningEffort | null, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadStartResponse.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadStartResponse.ts index fd84a41ae8..3885f61a34 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadStartResponse.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadStartResponse.ts @@ -6,6 +6,7 @@ import type { ReasoningEffort } from "../ReasoningEffort"; import type { ServiceTier } from "../ServiceTier"; import type { ApprovalsReviewer } from "./ApprovalsReviewer"; import type { AskForApproval } from "./AskForApproval"; +import type { PermissionProfile } from "./PermissionProfile"; import type { SandboxPolicy } from "./SandboxPolicy"; import type { Thread } from "./Thread"; @@ -17,4 +18,4 @@ instructionSources: Array, approvalPolicy: AskForApproval, /** * Reviewer currently used for approval requests on this thread. */ -approvalsReviewer: ApprovalsReviewer, sandbox: SandboxPolicy, reasoningEffort: ReasoningEffort | null, }; +approvalsReviewer: ApprovalsReviewer, sandbox: SandboxPolicy, permissionProfile: PermissionProfile, reasoningEffort: ReasoningEffort | null, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/index.ts b/codex-rs/app-server-protocol/schema/typescript/v2/index.ts index 7bddd0f9d6..8f081464cc 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/index.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/index.ts @@ -240,6 +240,8 @@ export type { OverriddenMetadata } from "./OverriddenMetadata"; export type { PatchApplyStatus } from "./PatchApplyStatus"; export type { PatchChangeKind } from "./PatchChangeKind"; export type { PermissionGrantScope } from "./PermissionGrantScope"; +export type { PermissionProfile } from "./PermissionProfile"; +export type { PermissionProfileFileSystemPermissions } from "./PermissionProfileFileSystemPermissions"; export type { PermissionsRequestApprovalParams } from "./PermissionsRequestApprovalParams"; export type { PermissionsRequestApprovalResponse } from "./PermissionsRequestApprovalResponse"; export type { PlanDeltaNotification } from "./PlanDeltaNotification"; diff --git a/codex-rs/app-server-protocol/src/protocol/common.rs b/codex-rs/app-server-protocol/src/protocol/common.rs index 4179e18b55..9e2635b4ea 100644 --- a/codex-rs/app-server-protocol/src/protocol/common.rs +++ b/codex-rs/app-server-protocol/src/protocol/common.rs @@ -1460,6 +1460,12 @@ mod tests { approval_policy: v2::AskForApproval::OnFailure, approvals_reviewer: v2::ApprovalsReviewer::User, sandbox: v2::SandboxPolicy::DangerFullAccess, + permission_profile: + codex_protocol::models::PermissionProfile::from_legacy_sandbox_policy( + &codex_protocol::protocol::SandboxPolicy::DangerFullAccess, + std::path::Path::new("/tmp"), + ) + .into(), reasoning_effort: None, }, }; @@ -1502,6 +1508,24 @@ mod tests { "sandbox": { "type": "dangerFullAccess" }, + "permissionProfile": { + "network": { + "enabled": true, + }, + "fileSystem": { + "entries": [ + { + "path": { + "type": "special", + "value": { + "kind": "root", + }, + }, + "access": "write", + }, + ], + }, + }, "reasoningEffort": null } }), diff --git a/codex-rs/app-server-protocol/src/protocol/v2.rs b/codex-rs/app-server-protocol/src/protocol/v2.rs index b7162eb4de..c558e002a1 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2.rs @@ -1383,6 +1383,70 @@ impl From for CoreFileSystemSandboxEntry { } } +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export_to = "v2/")] +pub struct PermissionProfileFileSystemPermissions { + pub entries: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub glob_scan_max_depth: Option, +} + +impl From for PermissionProfileFileSystemPermissions { + fn from(value: CoreFileSystemPermissions) -> Self { + Self { + entries: value + .entries + .into_iter() + .map(FileSystemSandboxEntry::from) + .collect(), + glob_scan_max_depth: value.glob_scan_max_depth, + } + } +} + +impl From for CoreFileSystemPermissions { + fn from(value: PermissionProfileFileSystemPermissions) -> Self { + Self { + entries: value + .entries + .into_iter() + .map(CoreFileSystemSandboxEntry::from) + .collect(), + glob_scan_max_depth: value.glob_scan_max_depth, + } + } +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export_to = "v2/")] +pub struct PermissionProfile { + pub network: Option, + pub file_system: Option, +} + +impl From for PermissionProfile { + fn from(value: CorePermissionProfile) -> Self { + Self { + network: value.network.map(AdditionalNetworkPermissions::from), + file_system: value + .file_system + .map(PermissionProfileFileSystemPermissions::from), + } + } +} + +impl From for CorePermissionProfile { + fn from(value: PermissionProfile) -> Self { + Self { + network: value.network.map(CoreNetworkPermissions::from), + file_system: value.file_system.map(CoreFileSystemPermissions::from), + } + } +} + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] @@ -3128,6 +3192,7 @@ pub struct ThreadStartResponse { /// Reviewer currently used for approval requests on this thread. pub approvals_reviewer: ApprovalsReviewer, pub sandbox: SandboxPolicy, + pub permission_profile: PermissionProfile, pub reasoning_effort: Option, } @@ -3217,6 +3282,7 @@ pub struct ThreadResumeResponse { /// Reviewer currently used for approval requests on this thread. pub approvals_reviewer: ApprovalsReviewer, pub sandbox: SandboxPolicy, + pub permission_profile: PermissionProfile, pub reasoning_effort: Option, } @@ -3297,6 +3363,7 @@ pub struct ThreadForkResponse { /// Reviewer currently used for approval requests on this thread. pub approvals_reviewer: ApprovalsReviewer, pub sandbox: SandboxPolicy, + pub permission_profile: PermissionProfile, pub reasoning_effort: Option, } @@ -7407,6 +7474,47 @@ mod tests { .expect_err("zero glob scan depth should fail deserialization"); } + #[test] + fn permission_profile_file_system_permissions_preserves_glob_scan_depth() { + let core_permissions = CoreFileSystemPermissions { + entries: vec![CoreFileSystemSandboxEntry { + path: CoreFileSystemPath::GlobPattern { + pattern: "**/*.env".to_string(), + }, + access: CoreFileSystemAccessMode::None, + }], + glob_scan_max_depth: NonZeroUsize::new(2), + }; + + let permissions = PermissionProfileFileSystemPermissions::from(core_permissions.clone()); + + assert_eq!( + permissions, + PermissionProfileFileSystemPermissions { + entries: vec![FileSystemSandboxEntry { + path: FileSystemPath::GlobPattern { + pattern: "**/*.env".to_string(), + }, + access: FileSystemAccessMode::None, + }], + glob_scan_max_depth: NonZeroUsize::new(2), + } + ); + assert_eq!( + CoreFileSystemPermissions::from(permissions), + core_permissions + ); + } + + #[test] + fn permission_profile_file_system_permissions_rejects_zero_glob_scan_depth() { + serde_json::from_value::(json!({ + "entries": [], + "globScanMaxDepth": 0, + })) + .expect_err("zero glob scan depth should fail deserialization"); + } + #[test] fn permissions_request_approval_response_uses_granted_permission_profile_without_macos() { let read_only_path = if cfg!(windows) { diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index 3b35f96236..34e9cc311e 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -2704,6 +2704,7 @@ impl CodexMessageProcessor { approval_policy: config_snapshot.approval_policy.into(), approvals_reviewer: config_snapshot.approvals_reviewer.into(), sandbox: config_snapshot.sandbox_policy.into(), + permission_profile: config_snapshot.permission_profile.into(), reasoning_effort: config_snapshot.reasoning_effort, }; if listener_task_context.general_analytics_enabled { @@ -4426,6 +4427,7 @@ impl CodexMessageProcessor { thread_status, /*has_live_in_progress_turn*/ false, ); + let permission_profile = codex_thread.config_snapshot().await.permission_profile; let response = ThreadResumeResponse { thread, @@ -4437,6 +4439,7 @@ impl CodexMessageProcessor { approval_policy: session_configured.approval_policy.into(), approvals_reviewer: session_configured.approvals_reviewer.into(), sandbox: session_configured.sandbox_policy.into(), + permission_profile: permission_profile.into(), reasoning_effort: session_configured.reasoning_effort, }; if self.config.features.enabled(Feature::GeneralAnalytics) { @@ -5071,6 +5074,7 @@ impl CodexMessageProcessor { .await, /*has_in_progress_turn*/ false, ); + let permission_profile = forked_thread.config_snapshot().await.permission_profile; let response = ThreadForkResponse { thread: thread.clone(), @@ -5082,6 +5086,7 @@ impl CodexMessageProcessor { approval_policy: session_configured.approval_policy.into(), approvals_reviewer: session_configured.approvals_reviewer.into(), sandbox: session_configured.sandbox_policy.into(), + permission_profile: permission_profile.into(), reasoning_effort: session_configured.reasoning_effort, }; if self.config.features.enabled(Feature::GeneralAnalytics) { @@ -8937,6 +8942,7 @@ async fn handle_pending_thread_resume_request( approval_policy, approvals_reviewer, sandbox_policy, + permission_profile, cwd, reasoning_effort, .. @@ -8952,6 +8958,7 @@ async fn handle_pending_thread_resume_request( approval_policy: approval_policy.into(), approvals_reviewer: approvals_reviewer.into(), sandbox: sandbox_policy.into(), + permission_profile: permission_profile.into(), reasoning_effort, }; let token_usage_thread = response.thread.clone();