Reduce environment sandboxing to permission profiles

This commit is contained in:
pakrym-oai
2026-07-20 13:44:00 -07:00
parent 2e5153cf4b
commit 4d4879ac10
66 changed files with 407 additions and 2322 deletions
+2 -382
View File
@@ -5,26 +5,6 @@
"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"
},
"ActivePermissionProfile": {
"properties": {
"extends": {
"default": null,
"description": "Parent profile identifier from the selected permissions profile's `extends` setting, when present.",
"type": [
"string",
"null"
]
},
"id": {
"description": "Identifier from `default_permissions` or the implicit built-in default, such as `:workspace` or a user-defined `[permissions.<id>]` profile.",
"type": "string"
}
},
"required": [
"id"
],
"type": "object"
},
"AddCreditsNudgeCreditType": {
"enum": [
"credits",
@@ -1032,210 +1012,6 @@
],
"type": "object"
},
"FileSystemAccessMode": {
"enum": [
"read",
"write",
"deny"
],
"type": "string"
},
"FileSystemPath": {
"oneOf": [
{
"properties": {
"path": {
"$ref": "#/definitions/LegacyAppPathString"
},
"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": [
"project_roots"
],
"type": "string"
},
"subpath": {
"anyOf": [
{
"$ref": "#/definitions/LegacyAppPathString"
},
{
"type": "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": {
"anyOf": [
{
"$ref": "#/definitions/LegacyAppPathString"
},
{
"type": "null"
}
]
}
},
"required": [
"kind",
"path"
],
"type": "object"
}
]
},
"FsCopyParams": {
"description": "Copy a file or directory tree on the host filesystem.",
"properties": {
@@ -4844,57 +4620,6 @@
],
"type": "object"
},
"TurnEnvironmentManagedFileSystemPermissionsParams": {
"oneOf": [
{
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
},
"type": "array"
},
"glob_scan_max_depth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"type": {
"enum": [
"restricted"
],
"title": "RestrictedTurnEnvironmentManagedFileSystemPermissionsParamsType",
"type": "string"
}
},
"required": [
"entries",
"type"
],
"title": "RestrictedTurnEnvironmentManagedFileSystemPermissionsParams",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"unrestricted"
],
"title": "UnrestrictedTurnEnvironmentManagedFileSystemPermissionsParamsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "UnrestrictedTurnEnvironmentManagedFileSystemPermissionsParams",
"type": "object"
}
]
},
"TurnEnvironmentParams": {
"properties": {
"cwd": {
@@ -4916,13 +4641,13 @@
"sandbox": {
"anyOf": [
{
"$ref": "#/definitions/TurnEnvironmentSandboxParams"
"$ref": "#/definitions/SandboxPolicy"
},
{
"type": "null"
}
],
"description": "Sandbox settings for this environment. Omitted inherits the thread sandbox settings."
"description": "Sandbox permissions for this environment. Omitted inherits the thread sandbox."
}
},
"required": [
@@ -4931,103 +4656,6 @@
],
"type": "object"
},
"TurnEnvironmentPermissionProfileParams": {
"oneOf": [
{
"properties": {
"file_system": {
"$ref": "#/definitions/TurnEnvironmentManagedFileSystemPermissionsParams"
},
"network": {
"$ref": "#/definitions/NetworkAccess"
},
"type": {
"enum": [
"managed"
],
"title": "ManagedTurnEnvironmentPermissionProfileParamsType",
"type": "string"
}
},
"required": [
"file_system",
"network",
"type"
],
"title": "ManagedTurnEnvironmentPermissionProfileParams",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"disabled"
],
"title": "DisabledTurnEnvironmentPermissionProfileParamsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DisabledTurnEnvironmentPermissionProfileParams",
"type": "object"
},
{
"properties": {
"network": {
"$ref": "#/definitions/NetworkAccess"
},
"type": {
"enum": [
"external"
],
"title": "ExternalTurnEnvironmentPermissionProfileParamsType",
"type": "string"
}
},
"required": [
"network",
"type"
],
"title": "ExternalTurnEnvironmentPermissionProfileParams",
"type": "object"
}
]
},
"TurnEnvironmentSandboxParams": {
"properties": {
"activePermissionProfile": {
"anyOf": [
{
"$ref": "#/definitions/ActivePermissionProfile"
},
{
"type": "null"
}
]
},
"permissionProfile": {
"$ref": "#/definitions/TurnEnvironmentPermissionProfileParams"
},
"useLegacyLandlock": {
"default": false,
"type": "boolean"
},
"windowsSandboxLevel": {
"$ref": "#/definitions/WindowsSandboxLevel"
},
"windowsSandboxPrivateDesktop": {
"default": false,
"type": "boolean"
}
},
"required": [
"permissionProfile",
"windowsSandboxLevel"
],
"type": "object"
},
"TurnInterruptParams": {
"properties": {
"threadId": {
@@ -5393,14 +5021,6 @@
}
]
},
"WindowsSandboxLevel": {
"enum": [
"disabled",
"restricted-token",
"elevated"
],
"type": "string"
},
"WindowsSandboxSetupMode": {
"enum": [
"elevated",
@@ -20828,57 +20828,6 @@
"title": "TurnDiffUpdatedNotification",
"type": "object"
},
"TurnEnvironmentManagedFileSystemPermissionsParams": {
"oneOf": [
{
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/v2/FileSystemSandboxEntry"
},
"type": "array"
},
"glob_scan_max_depth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"type": {
"enum": [
"restricted"
],
"title": "RestrictedTurnEnvironmentManagedFileSystemPermissionsParamsType",
"type": "string"
}
},
"required": [
"entries",
"type"
],
"title": "RestrictedTurnEnvironmentManagedFileSystemPermissionsParams",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"unrestricted"
],
"title": "UnrestrictedTurnEnvironmentManagedFileSystemPermissionsParamsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "UnrestrictedTurnEnvironmentManagedFileSystemPermissionsParams",
"type": "object"
}
]
},
"TurnEnvironmentParams": {
"properties": {
"cwd": {
@@ -20900,13 +20849,13 @@
"sandbox": {
"anyOf": [
{
"$ref": "#/definitions/v2/TurnEnvironmentSandboxParams"
"$ref": "#/definitions/v2/SandboxPolicy"
},
{
"type": "null"
}
],
"description": "Sandbox settings for this environment. Omitted inherits the thread sandbox settings."
"description": "Sandbox permissions for this environment. Omitted inherits the thread sandbox."
}
},
"required": [
@@ -20915,103 +20864,6 @@
],
"type": "object"
},
"TurnEnvironmentPermissionProfileParams": {
"oneOf": [
{
"properties": {
"file_system": {
"$ref": "#/definitions/v2/TurnEnvironmentManagedFileSystemPermissionsParams"
},
"network": {
"$ref": "#/definitions/v2/NetworkAccess"
},
"type": {
"enum": [
"managed"
],
"title": "ManagedTurnEnvironmentPermissionProfileParamsType",
"type": "string"
}
},
"required": [
"file_system",
"network",
"type"
],
"title": "ManagedTurnEnvironmentPermissionProfileParams",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"disabled"
],
"title": "DisabledTurnEnvironmentPermissionProfileParamsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DisabledTurnEnvironmentPermissionProfileParams",
"type": "object"
},
{
"properties": {
"network": {
"$ref": "#/definitions/v2/NetworkAccess"
},
"type": {
"enum": [
"external"
],
"title": "ExternalTurnEnvironmentPermissionProfileParamsType",
"type": "string"
}
},
"required": [
"network",
"type"
],
"title": "ExternalTurnEnvironmentPermissionProfileParams",
"type": "object"
}
]
},
"TurnEnvironmentSandboxParams": {
"properties": {
"activePermissionProfile": {
"anyOf": [
{
"$ref": "#/definitions/v2/ActivePermissionProfile"
},
{
"type": "null"
}
]
},
"permissionProfile": {
"$ref": "#/definitions/v2/TurnEnvironmentPermissionProfileParams"
},
"useLegacyLandlock": {
"default": false,
"type": "boolean"
},
"windowsSandboxLevel": {
"$ref": "#/definitions/v2/WindowsSandboxLevel"
},
"windowsSandboxPrivateDesktop": {
"default": false,
"type": "boolean"
}
},
"required": [
"permissionProfile",
"windowsSandboxLevel"
],
"type": "object"
},
"TurnError": {
"properties": {
"additionalDetails": {
@@ -21779,14 +21631,6 @@
},
"type": "object"
},
"WindowsSandboxLevel": {
"enum": [
"disabled",
"restricted-token",
"elevated"
],
"type": "string"
},
"WindowsSandboxReadiness": {
"enum": [
"ready",
@@ -18607,57 +18607,6 @@
"title": "TurnDiffUpdatedNotification",
"type": "object"
},
"TurnEnvironmentManagedFileSystemPermissionsParams": {
"oneOf": [
{
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
},
"type": "array"
},
"glob_scan_max_depth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"type": {
"enum": [
"restricted"
],
"title": "RestrictedTurnEnvironmentManagedFileSystemPermissionsParamsType",
"type": "string"
}
},
"required": [
"entries",
"type"
],
"title": "RestrictedTurnEnvironmentManagedFileSystemPermissionsParams",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"unrestricted"
],
"title": "UnrestrictedTurnEnvironmentManagedFileSystemPermissionsParamsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "UnrestrictedTurnEnvironmentManagedFileSystemPermissionsParams",
"type": "object"
}
]
},
"TurnEnvironmentParams": {
"properties": {
"cwd": {
@@ -18679,13 +18628,13 @@
"sandbox": {
"anyOf": [
{
"$ref": "#/definitions/TurnEnvironmentSandboxParams"
"$ref": "#/definitions/SandboxPolicy"
},
{
"type": "null"
}
],
"description": "Sandbox settings for this environment. Omitted inherits the thread sandbox settings."
"description": "Sandbox permissions for this environment. Omitted inherits the thread sandbox."
}
},
"required": [
@@ -18694,103 +18643,6 @@
],
"type": "object"
},
"TurnEnvironmentPermissionProfileParams": {
"oneOf": [
{
"properties": {
"file_system": {
"$ref": "#/definitions/TurnEnvironmentManagedFileSystemPermissionsParams"
},
"network": {
"$ref": "#/definitions/NetworkAccess"
},
"type": {
"enum": [
"managed"
],
"title": "ManagedTurnEnvironmentPermissionProfileParamsType",
"type": "string"
}
},
"required": [
"file_system",
"network",
"type"
],
"title": "ManagedTurnEnvironmentPermissionProfileParams",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"disabled"
],
"title": "DisabledTurnEnvironmentPermissionProfileParamsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DisabledTurnEnvironmentPermissionProfileParams",
"type": "object"
},
{
"properties": {
"network": {
"$ref": "#/definitions/NetworkAccess"
},
"type": {
"enum": [
"external"
],
"title": "ExternalTurnEnvironmentPermissionProfileParamsType",
"type": "string"
}
},
"required": [
"network",
"type"
],
"title": "ExternalTurnEnvironmentPermissionProfileParams",
"type": "object"
}
]
},
"TurnEnvironmentSandboxParams": {
"properties": {
"activePermissionProfile": {
"anyOf": [
{
"$ref": "#/definitions/ActivePermissionProfile"
},
{
"type": "null"
}
]
},
"permissionProfile": {
"$ref": "#/definitions/TurnEnvironmentPermissionProfileParams"
},
"useLegacyLandlock": {
"default": false,
"type": "boolean"
},
"windowsSandboxLevel": {
"$ref": "#/definitions/WindowsSandboxLevel"
},
"windowsSandboxPrivateDesktop": {
"default": false,
"type": "boolean"
}
},
"required": [
"permissionProfile",
"windowsSandboxLevel"
],
"type": "object"
},
"TurnError": {
"properties": {
"additionalDetails": {
@@ -19558,14 +19410,6 @@
},
"type": "object"
},
"WindowsSandboxLevel": {
"enum": [
"disabled",
"restricted-token",
"elevated"
],
"type": "string"
},
"WindowsSandboxReadiness": {
"enum": [
"ready",
@@ -5,26 +5,6 @@
"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"
},
"ActivePermissionProfile": {
"properties": {
"extends": {
"default": null,
"description": "Parent profile identifier from the selected permissions profile's `extends` setting, when present.",
"type": [
"string",
"null"
]
},
"id": {
"description": "Identifier from `default_permissions` or the implicit built-in default, such as `:workspace` or a user-defined `[permissions.<id>]` profile.",
"type": "string"
}
},
"required": [
"id"
],
"type": "object"
},
"ApprovalsReviewer": {
"description": "Configures who approval requests are routed to for review. Examples include sandbox escapes, blocked network access, MCP approval prompts, and ARC escalations. Defaults to `user`. `auto_review` uses a carefully prompted subagent to gather relevant context and apply a risk-based decision framework before approving or denying the request. The legacy value `guardian_subagent` is accepted for compatibility.",
"enum": [
@@ -211,210 +191,6 @@
}
]
},
"FileSystemAccessMode": {
"enum": [
"read",
"write",
"deny"
],
"type": "string"
},
"FileSystemPath": {
"oneOf": [
{
"properties": {
"path": {
"$ref": "#/definitions/LegacyAppPathString"
},
"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": [
"project_roots"
],
"type": "string"
},
"subpath": {
"anyOf": [
{
"$ref": "#/definitions/LegacyAppPathString"
},
{
"type": "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": {
"anyOf": [
{
"$ref": "#/definitions/LegacyAppPathString"
},
{
"type": "null"
}
]
}
},
"required": [
"kind",
"path"
],
"type": "object"
}
]
},
"LegacyAppPathString": {
"type": "string"
},
@@ -466,6 +242,105 @@
],
"type": "string"
},
"SandboxPolicy": {
"oneOf": [
{
"properties": {
"type": {
"enum": [
"dangerFullAccess"
],
"title": "DangerFullAccessSandboxPolicyType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DangerFullAccessSandboxPolicy",
"type": "object"
},
{
"properties": {
"networkAccess": {
"default": false,
"type": "boolean"
},
"type": {
"enum": [
"readOnly"
],
"title": "ReadOnlySandboxPolicyType",
"type": "string"
}
},
"required": [
"type"
],
"title": "ReadOnlySandboxPolicy",
"type": "object"
},
{
"properties": {
"networkAccess": {
"allOf": [
{
"$ref": "#/definitions/NetworkAccess"
}
],
"default": "restricted"
},
"type": {
"enum": [
"externalSandbox"
],
"title": "ExternalSandboxSandboxPolicyType",
"type": "string"
}
},
"required": [
"type"
],
"title": "ExternalSandboxSandboxPolicy",
"type": "object"
},
{
"properties": {
"excludeSlashTmp": {
"default": false,
"type": "boolean"
},
"excludeTmpdirEnvVar": {
"default": false,
"type": "boolean"
},
"networkAccess": {
"default": false,
"type": "boolean"
},
"type": {
"enum": [
"workspaceWrite"
],
"title": "WorkspaceWriteSandboxPolicyType",
"type": "string"
},
"writableRoots": {
"default": [],
"items": {
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": "array"
}
},
"required": [
"type"
],
"title": "WorkspaceWriteSandboxPolicy",
"type": "object"
}
]
},
"SelectedCapabilityRoot": {
"description": "A user-selected root that can expose one or more runtime capabilities.",
"properties": {
@@ -505,57 +380,6 @@
],
"type": "string"
},
"TurnEnvironmentManagedFileSystemPermissionsParams": {
"oneOf": [
{
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
},
"type": "array"
},
"glob_scan_max_depth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"type": {
"enum": [
"restricted"
],
"title": "RestrictedTurnEnvironmentManagedFileSystemPermissionsParamsType",
"type": "string"
}
},
"required": [
"entries",
"type"
],
"title": "RestrictedTurnEnvironmentManagedFileSystemPermissionsParams",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"unrestricted"
],
"title": "UnrestrictedTurnEnvironmentManagedFileSystemPermissionsParamsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "UnrestrictedTurnEnvironmentManagedFileSystemPermissionsParams",
"type": "object"
}
]
},
"TurnEnvironmentParams": {
"properties": {
"cwd": {
@@ -577,13 +401,13 @@
"sandbox": {
"anyOf": [
{
"$ref": "#/definitions/TurnEnvironmentSandboxParams"
"$ref": "#/definitions/SandboxPolicy"
},
{
"type": "null"
}
],
"description": "Sandbox settings for this environment. Omitted inherits the thread sandbox settings."
"description": "Sandbox permissions for this environment. Omitted inherits the thread sandbox."
}
},
"required": [
@@ -591,111 +415,6 @@
"environmentId"
],
"type": "object"
},
"TurnEnvironmentPermissionProfileParams": {
"oneOf": [
{
"properties": {
"file_system": {
"$ref": "#/definitions/TurnEnvironmentManagedFileSystemPermissionsParams"
},
"network": {
"$ref": "#/definitions/NetworkAccess"
},
"type": {
"enum": [
"managed"
],
"title": "ManagedTurnEnvironmentPermissionProfileParamsType",
"type": "string"
}
},
"required": [
"file_system",
"network",
"type"
],
"title": "ManagedTurnEnvironmentPermissionProfileParams",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"disabled"
],
"title": "DisabledTurnEnvironmentPermissionProfileParamsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DisabledTurnEnvironmentPermissionProfileParams",
"type": "object"
},
{
"properties": {
"network": {
"$ref": "#/definitions/NetworkAccess"
},
"type": {
"enum": [
"external"
],
"title": "ExternalTurnEnvironmentPermissionProfileParamsType",
"type": "string"
}
},
"required": [
"network",
"type"
],
"title": "ExternalTurnEnvironmentPermissionProfileParams",
"type": "object"
}
]
},
"TurnEnvironmentSandboxParams": {
"properties": {
"activePermissionProfile": {
"anyOf": [
{
"$ref": "#/definitions/ActivePermissionProfile"
},
{
"type": "null"
}
]
},
"permissionProfile": {
"$ref": "#/definitions/TurnEnvironmentPermissionProfileParams"
},
"useLegacyLandlock": {
"default": false,
"type": "boolean"
},
"windowsSandboxLevel": {
"$ref": "#/definitions/WindowsSandboxLevel"
},
"windowsSandboxPrivateDesktop": {
"default": false,
"type": "boolean"
}
},
"required": [
"permissionProfile",
"windowsSandboxLevel"
],
"type": "object"
},
"WindowsSandboxLevel": {
"enum": [
"disabled",
"restricted-token",
"elevated"
],
"type": "string"
}
},
"properties": {
@@ -5,26 +5,6 @@
"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"
},
"ActivePermissionProfile": {
"properties": {
"extends": {
"default": null,
"description": "Parent profile identifier from the selected permissions profile's `extends` setting, when present.",
"type": [
"string",
"null"
]
},
"id": {
"description": "Identifier from `default_permissions` or the implicit built-in default, such as `:workspace` or a user-defined `[permissions.<id>]` profile.",
"type": "string"
}
},
"required": [
"id"
],
"type": "object"
},
"AdditionalContextEntry": {
"properties": {
"kind": {
@@ -140,210 +120,6 @@
],
"type": "object"
},
"FileSystemAccessMode": {
"enum": [
"read",
"write",
"deny"
],
"type": "string"
},
"FileSystemPath": {
"oneOf": [
{
"properties": {
"path": {
"$ref": "#/definitions/LegacyAppPathString"
},
"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": [
"project_roots"
],
"type": "string"
},
"subpath": {
"anyOf": [
{
"$ref": "#/definitions/LegacyAppPathString"
},
{
"type": "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": {
"anyOf": [
{
"$ref": "#/definitions/LegacyAppPathString"
},
{
"type": "null"
}
]
}
},
"required": [
"kind",
"path"
],
"type": "object"
}
]
},
"ImageDetail": {
"enum": [
"auto",
@@ -579,57 +355,6 @@
],
"type": "object"
},
"TurnEnvironmentManagedFileSystemPermissionsParams": {
"oneOf": [
{
"properties": {
"entries": {
"items": {
"$ref": "#/definitions/FileSystemSandboxEntry"
},
"type": "array"
},
"glob_scan_max_depth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"type": {
"enum": [
"restricted"
],
"title": "RestrictedTurnEnvironmentManagedFileSystemPermissionsParamsType",
"type": "string"
}
},
"required": [
"entries",
"type"
],
"title": "RestrictedTurnEnvironmentManagedFileSystemPermissionsParams",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"unrestricted"
],
"title": "UnrestrictedTurnEnvironmentManagedFileSystemPermissionsParamsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "UnrestrictedTurnEnvironmentManagedFileSystemPermissionsParams",
"type": "object"
}
]
},
"TurnEnvironmentParams": {
"properties": {
"cwd": {
@@ -651,13 +376,13 @@
"sandbox": {
"anyOf": [
{
"$ref": "#/definitions/TurnEnvironmentSandboxParams"
"$ref": "#/definitions/SandboxPolicy"
},
{
"type": "null"
}
],
"description": "Sandbox settings for this environment. Omitted inherits the thread sandbox settings."
"description": "Sandbox permissions for this environment. Omitted inherits the thread sandbox."
}
},
"required": [
@@ -666,103 +391,6 @@
],
"type": "object"
},
"TurnEnvironmentPermissionProfileParams": {
"oneOf": [
{
"properties": {
"file_system": {
"$ref": "#/definitions/TurnEnvironmentManagedFileSystemPermissionsParams"
},
"network": {
"$ref": "#/definitions/NetworkAccess"
},
"type": {
"enum": [
"managed"
],
"title": "ManagedTurnEnvironmentPermissionProfileParamsType",
"type": "string"
}
},
"required": [
"file_system",
"network",
"type"
],
"title": "ManagedTurnEnvironmentPermissionProfileParams",
"type": "object"
},
{
"properties": {
"type": {
"enum": [
"disabled"
],
"title": "DisabledTurnEnvironmentPermissionProfileParamsType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DisabledTurnEnvironmentPermissionProfileParams",
"type": "object"
},
{
"properties": {
"network": {
"$ref": "#/definitions/NetworkAccess"
},
"type": {
"enum": [
"external"
],
"title": "ExternalTurnEnvironmentPermissionProfileParamsType",
"type": "string"
}
},
"required": [
"network",
"type"
],
"title": "ExternalTurnEnvironmentPermissionProfileParams",
"type": "object"
}
]
},
"TurnEnvironmentSandboxParams": {
"properties": {
"activePermissionProfile": {
"anyOf": [
{
"$ref": "#/definitions/ActivePermissionProfile"
},
{
"type": "null"
}
]
},
"permissionProfile": {
"$ref": "#/definitions/TurnEnvironmentPermissionProfileParams"
},
"useLegacyLandlock": {
"default": false,
"type": "boolean"
},
"windowsSandboxLevel": {
"$ref": "#/definitions/WindowsSandboxLevel"
},
"windowsSandboxPrivateDesktop": {
"default": false,
"type": "boolean"
}
},
"required": [
"permissionProfile",
"windowsSandboxLevel"
],
"type": "object"
},
"UserInput": {
"oneOf": [
{
@@ -944,14 +572,6 @@
"type": "object"
}
]
},
"WindowsSandboxLevel": {
"enum": [
"disabled",
"restricted-token",
"elevated"
],
"type": "string"
}
},
"properties": {
@@ -1,5 +0,0 @@
// 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.
export type WindowsSandboxLevel = "disabled" | "restricted-token" | "elevated";
-1
View File
@@ -89,5 +89,4 @@ export type { WebSearchItem } from "./WebSearchItem";
export type { WebSearchLocation } from "./WebSearchLocation";
export type { WebSearchMode } from "./WebSearchMode";
export type { WebSearchToolConfig } from "./WebSearchToolConfig";
export type { WindowsSandboxLevel } from "./WindowsSandboxLevel";
export * as v2 from "./v2";
@@ -1,6 +0,0 @@
// 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 TurnEnvironmentManagedFileSystemPermissionsParams = { "type": "restricted", entries: Array<FileSystemSandboxEntry>, glob_scan_max_depth?: number, } | { "type": "unrestricted" };
@@ -2,7 +2,7 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { LegacyAppPathString } from "../LegacyAppPathString";
import type { TurnEnvironmentSandboxParams } from "./TurnEnvironmentSandboxParams";
import type { SandboxPolicy } from "./SandboxPolicy";
export type TurnEnvironmentParams = { environmentId: string, cwd: LegacyAppPathString,
/**
@@ -10,7 +10,6 @@ export type TurnEnvironmentParams = { environmentId: string, cwd: LegacyAppPathS
*/
runtimeWorkspaceRoots?: Array<LegacyAppPathString> | null,
/**
* Sandbox settings for this environment. Omitted inherits the thread
* sandbox settings.
* Sandbox permissions for this environment. Omitted inherits the thread sandbox.
*/
sandbox?: TurnEnvironmentSandboxParams | null, };
sandbox?: SandboxPolicy | null, };
@@ -1,7 +0,0 @@
// 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 { NetworkAccess } from "./NetworkAccess";
import type { TurnEnvironmentManagedFileSystemPermissionsParams } from "./TurnEnvironmentManagedFileSystemPermissionsParams";
export type TurnEnvironmentPermissionProfileParams = { "type": "managed", file_system: TurnEnvironmentManagedFileSystemPermissionsParams, network: NetworkAccess, } | { "type": "disabled" } | { "type": "external", network: NetworkAccess, };
@@ -1,8 +0,0 @@
// 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 { WindowsSandboxLevel } from "../WindowsSandboxLevel";
import type { ActivePermissionProfile } from "./ActivePermissionProfile";
import type { TurnEnvironmentPermissionProfileParams } from "./TurnEnvironmentPermissionProfileParams";
export type TurnEnvironmentSandboxParams = { permissionProfile: TurnEnvironmentPermissionProfileParams, activePermissionProfile?: ActivePermissionProfile | null, windowsSandboxLevel: WindowsSandboxLevel, windowsSandboxPrivateDesktop: boolean, useLegacyLandlock: boolean, };
@@ -496,10 +496,7 @@ export type { ToolsV2 } from "./ToolsV2";
export type { Turn } from "./Turn";
export type { TurnCompletedNotification } from "./TurnCompletedNotification";
export type { TurnDiffUpdatedNotification } from "./TurnDiffUpdatedNotification";
export type { TurnEnvironmentManagedFileSystemPermissionsParams } from "./TurnEnvironmentManagedFileSystemPermissionsParams";
export type { TurnEnvironmentParams } from "./TurnEnvironmentParams";
export type { TurnEnvironmentPermissionProfileParams } from "./TurnEnvironmentPermissionProfileParams";
export type { TurnEnvironmentSandboxParams } from "./TurnEnvironmentSandboxParams";
export type { TurnError } from "./TurnError";
export type { TurnInterruptParams } from "./TurnInterruptParams";
export type { TurnInterruptResponse } from "./TurnInterruptResponse";
@@ -4304,8 +4304,7 @@ fn turn_start_params_round_trip_environments() {
"cwd": cwd,
"runtimeWorkspaceRoots": [workspace_root],
"sandbox": {
"permissionProfile": {"type": "disabled"},
"windowsSandboxLevel": "restricted-token"
"type": "readOnly"
}
}
],
@@ -4318,13 +4317,8 @@ fn turn_start_params_round_trip_environments() {
environment_id: "local".to_string(),
cwd: cwd.clone(),
runtime_workspace_roots: Some(vec![workspace_root.clone()]),
sandbox: Some(TurnEnvironmentSandboxParams {
permission_profile: TurnEnvironmentPermissionProfileParams::Disabled,
active_permission_profile: None,
windows_sandbox_level:
codex_protocol::config_types::WindowsSandboxLevel::RestrictedToken,
windows_sandbox_private_desktop: false,
use_legacy_landlock: false,
sandbox: Some(SandboxPolicy::ReadOnly {
network_access: false,
}),
}])
);
@@ -4342,10 +4336,8 @@ fn turn_start_params_round_trip_environments() {
"cwd": cwd,
"runtimeWorkspaceRoots": [workspace_root],
"sandbox": {
"permissionProfile": {"type": "disabled"},
"windowsSandboxLevel": "restricted-token",
"windowsSandboxPrivateDesktop": false,
"useLegacyLandlock": false
"type": "readOnly",
"networkAccess": false
}
}
]))
@@ -1,8 +1,5 @@
use super::ActivePermissionProfile;
use super::ApprovalsReviewer;
use super::AskForApproval;
use super::FileSystemSandboxEntry;
use super::NetworkAccess;
use super::SandboxPolicy;
use super::Turn;
use codex_experimental_api_macros::ExperimentalApi;
@@ -10,12 +7,8 @@ use codex_protocol::config_types::CollaborationMode;
use codex_protocol::config_types::MultiAgentMode;
use codex_protocol::config_types::Personality;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::ImageDetail;
use codex_protocol::models::ManagedFileSystemPermissions as CoreManagedFileSystemPermissions;
use codex_protocol::models::PermissionProfile as CorePermissionProfile;
use codex_protocol::openai_models::ReasoningEffort;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::plan_tool::PlanItemArg as CorePlanItemArg;
use codex_protocol::plan_tool::StepStatus as CorePlanStepStatus;
use codex_protocol::user_input::ByteRange as CoreByteRange;
@@ -28,8 +21,6 @@ use serde::Deserialize;
use serde::Serialize;
use serde_json::Value as JsonValue;
use std::collections::HashMap;
use std::io;
use std::num::NonZeroUsize;
use std::path::PathBuf;
use ts_rs::TS;
@@ -53,168 +44,9 @@ pub struct TurnEnvironmentParams {
/// Environment-native runtime workspace roots. Omitted defaults to `cwd`.
#[ts(optional = nullable)]
pub runtime_workspace_roots: Option<Vec<LegacyAppPathString>>,
/// Sandbox settings for this environment. Omitted inherits the thread
/// sandbox settings.
#[serde(default, skip_serializing_if = "Option::is_none")]
/// Sandbox permissions for this environment. Omitted inherits the thread sandbox.
#[ts(optional = nullable)]
pub sandbox: Option<TurnEnvironmentSandboxParams>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct TurnEnvironmentSandboxParams {
pub permission_profile: TurnEnvironmentPermissionProfileParams,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional = nullable)]
pub active_permission_profile: Option<ActivePermissionProfile>,
pub windows_sandbox_level: WindowsSandboxLevel,
#[serde(default)]
pub windows_sandbox_private_desktop: bool,
#[serde(default)]
pub use_legacy_landlock: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(tag = "type", rename_all = "snake_case")]
#[ts(tag = "type", export_to = "v2/")]
pub enum TurnEnvironmentPermissionProfileParams {
#[serde(rename_all = "snake_case")]
#[ts(rename_all = "snake_case")]
Managed {
file_system: TurnEnvironmentManagedFileSystemPermissionsParams,
network: NetworkAccess,
},
Disabled,
#[serde(rename_all = "snake_case")]
#[ts(rename_all = "snake_case")]
External {
network: NetworkAccess,
},
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(tag = "type", rename_all = "snake_case")]
#[ts(tag = "type", export_to = "v2/")]
pub enum TurnEnvironmentManagedFileSystemPermissionsParams {
#[serde(rename_all = "snake_case")]
#[ts(rename_all = "snake_case")]
Restricted {
entries: Vec<FileSystemSandboxEntry>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
glob_scan_max_depth: Option<NonZeroUsize>,
},
Unrestricted,
}
impl TryFrom<TurnEnvironmentSandboxParams> for codex_protocol::protocol::TurnEnvironmentSandbox {
type Error = io::Error;
fn try_from(value: TurnEnvironmentSandboxParams) -> Result<Self, Self::Error> {
Ok(Self {
permission_profile: value.permission_profile.try_into()?,
active_permission_profile: value.active_permission_profile.map(Into::into),
windows_sandbox_level: value.windows_sandbox_level,
windows_sandbox_private_desktop: value.windows_sandbox_private_desktop,
use_legacy_landlock: value.use_legacy_landlock,
})
}
}
impl TryFrom<TurnEnvironmentPermissionProfileParams> for CorePermissionProfile {
type Error = io::Error;
fn try_from(value: TurnEnvironmentPermissionProfileParams) -> Result<Self, Self::Error> {
Ok(match value {
TurnEnvironmentPermissionProfileParams::Managed {
file_system,
network,
} => Self::Managed {
file_system: file_system.try_into()?,
network: network.into(),
},
TurnEnvironmentPermissionProfileParams::Disabled => Self::Disabled,
TurnEnvironmentPermissionProfileParams::External { network } => Self::External {
network: network.into(),
},
})
}
}
impl From<CorePermissionProfile> for TurnEnvironmentPermissionProfileParams {
fn from(value: CorePermissionProfile) -> Self {
match value {
CorePermissionProfile::Managed {
file_system,
network,
} => Self::Managed {
file_system: file_system.into(),
network: network.into(),
},
CorePermissionProfile::Disabled => Self::Disabled,
CorePermissionProfile::External { network } => Self::External {
network: network.into(),
},
}
}
}
impl TryFrom<TurnEnvironmentManagedFileSystemPermissionsParams>
for CoreManagedFileSystemPermissions
{
type Error = io::Error;
fn try_from(
value: TurnEnvironmentManagedFileSystemPermissionsParams,
) -> Result<Self, Self::Error> {
Ok(match value {
TurnEnvironmentManagedFileSystemPermissionsParams::Restricted {
entries,
glob_scan_max_depth,
} => Self::Restricted {
entries: entries
.into_iter()
.map(TryInto::try_into)
.collect::<io::Result<Vec<_>>>()?,
glob_scan_max_depth,
},
TurnEnvironmentManagedFileSystemPermissionsParams::Unrestricted => Self::Unrestricted,
})
}
}
impl From<CoreManagedFileSystemPermissions> for TurnEnvironmentManagedFileSystemPermissionsParams {
fn from(value: CoreManagedFileSystemPermissions) -> Self {
match value {
CoreManagedFileSystemPermissions::Restricted {
entries,
glob_scan_max_depth,
} => Self::Restricted {
entries: entries.into_iter().map(Into::into).collect(),
glob_scan_max_depth,
},
CoreManagedFileSystemPermissions::Unrestricted => Self::Unrestricted,
}
}
}
impl From<NetworkAccess> for NetworkSandboxPolicy {
fn from(value: NetworkAccess) -> Self {
match value {
NetworkAccess::Restricted => Self::Restricted,
NetworkAccess::Enabled => Self::Enabled,
}
}
}
impl From<NetworkSandboxPolicy> for NetworkAccess {
fn from(value: NetworkSandboxPolicy) -> Self {
match value {
NetworkSandboxPolicy::Restricted => Self::Restricted,
NetworkSandboxPolicy::Enabled => Self::Enabled,
}
}
pub sandbox: Option<SandboxPolicy>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
@@ -427,6 +427,7 @@ use codex_protocol::error::CodexErr;
use codex_protocol::error::Result as CodexResult;
#[cfg(test)]
use codex_protocol::items::TurnItem;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::ResponseItem;
use codex_protocol::openai_models::ReasoningEffort;
#[cfg(test)]
@@ -591,15 +592,9 @@ fn resolve_turn_environment_selections(
let mut selections = Vec::with_capacity(environments.len());
for environment in environments {
let environment_id = environment.environment_id;
let sandbox = environment
let permission_profile = environment
.sandbox
.map(TryInto::try_into)
.transpose()
.map_err(|err| {
invalid_request(format!(
"invalid sandbox for environment `{environment_id}`: {err}"
))
})?;
.map(|sandbox| PermissionProfile::from_legacy_sandbox_policy(&sandbox.to_core()));
let cwd = environment
.cwd
.to_inferred_path_uri()
@@ -631,7 +626,7 @@ fn resolve_turn_environment_selections(
environment_id,
cwd,
workspace_roots,
sandbox,
permission_profile,
});
}
thread_manager
@@ -198,15 +198,7 @@ impl TestAppServer {
environment_id: selection.environment_id.clone(),
cwd: selection.cwd.clone().into(),
runtime_workspace_roots: None,
sandbox: selection.sandbox.clone().map(|sandbox| {
codex_app_server_protocol::TurnEnvironmentSandboxParams {
permission_profile: sandbox.permission_profile.into(),
active_permission_profile: sandbox.active_permission_profile.map(Into::into),
windows_sandbox_level: sandbox.windows_sandbox_level,
windows_sandbox_private_desktop: sandbox.windows_sandbox_private_desktop,
use_legacy_landlock: sandbox.use_legacy_landlock,
}
}),
sandbox: None,
})
}
+1 -1
View File
@@ -44,7 +44,7 @@ pub(crate) async fn apply_patch(
turn_environment.permission_profile(),
file_system_sandbox_policy,
&action.cwd,
turn_environment.settings.sandbox.windows_sandbox_level,
turn_context.windows_sandbox_level,
) {
SafetyCheck::AutoApprove {
user_explicitly_approved,
@@ -6,6 +6,7 @@ use crate::context::environment_context::NetworkContext;
use crate::context::environment_context::push_xml_escaped_text;
use crate::environment_selection::TurnEnvironmentSnapshot;
use crate::session::turn_context::TurnContext;
use crate::session::turn_context::TurnEnvironment;
use codex_utils_path_uri::PathUri;
use serde::Deserialize;
use serde::Serialize;
@@ -18,6 +19,7 @@ pub(crate) struct EnvironmentsState {
current_date: Option<String>,
timezone: Option<String>,
network: Option<NetworkContext>,
filesystem: Option<FileSystemContext>,
subagents: Option<String>,
}
@@ -26,11 +28,19 @@ impl EnvironmentsState {
turn_context: &TurnContext,
environments: &TurnEnvironmentSnapshot,
) -> Self {
let workspace_roots = environments
.primary()
.map(TurnEnvironment::workspace_roots)
.unwrap_or_default();
Self {
environments: environment_states(environments),
current_date: turn_context.current_date.clone(),
timezone: turn_context.timezone.clone(),
network: network_from_turn_context(turn_context),
filesystem: Some(FileSystemContext::from_permission_profile(
turn_context.config.permissions.permission_profile(),
workspace_roots,
)),
subagents: None,
}
}
@@ -43,7 +53,6 @@ impl EnvironmentsState {
}
fn rendered_full(&self) -> RenderedEnvironments {
let legacy_single = is_legacy_single(&self.environments);
RenderedEnvironments {
updates: self
.environments
@@ -52,18 +61,11 @@ impl EnvironmentsState {
(id.clone(), EnvironmentUpdate::Current(environment.clone()))
})
.collect(),
legacy_single,
legacy_single: is_legacy_single(&self.environments),
current_date: self.current_date.clone(),
timezone: self.timezone.clone(),
network: self.network.clone(),
filesystem: legacy_single
.then(|| {
self.environments
.values()
.next()
.and_then(|environment| environment.filesystem.clone())
})
.flatten(),
filesystem: self.filesystem.clone(),
subagents: self.subagents.clone(),
}
}
@@ -74,7 +76,6 @@ impl WorldStateSection for EnvironmentsState {
type Snapshot = EnvironmentsSnapshot;
fn snapshot(&self) -> Self::Snapshot {
let legacy_single = is_legacy_single(&self.environments);
EnvironmentsSnapshot {
environments: self
.environments
@@ -86,14 +87,6 @@ impl WorldStateSection for EnvironmentsState {
cwd: environment.cwd.inferred_native_path_string(),
status: environment.status,
shell: environment.shell.clone(),
filesystem: (!legacy_single)
.then(|| {
environment
.filesystem
.as_ref()
.map(FileSystemContext::render)
})
.flatten(),
},
)
})
@@ -101,16 +94,7 @@ impl WorldStateSection for EnvironmentsState {
current_date: self.current_date.clone(),
timezone: self.timezone.clone(),
network: self.network.as_ref().map(NetworkContext::render),
filesystem: legacy_single
.then(|| {
self.environments.values().next().and_then(|environment| {
environment
.filesystem
.as_ref()
.map(FileSystemContext::render)
})
})
.flatten(),
filesystem: self.filesystem.as_ref().map(FileSystemContext::render),
subagents: self.subagents.clone(),
}
}
@@ -159,14 +143,7 @@ impl WorldStateSection for EnvironmentsState {
current_date: self.current_date.clone(),
timezone: self.timezone.clone(),
network: self.network.clone(),
filesystem: legacy_single
.then(|| {
self.environments
.values()
.next()
.and_then(|environment| environment.filesystem.clone())
})
.flatten(),
filesystem: self.filesystem.clone(),
subagents: self.subagents.clone(),
}) as Box<dyn ContextualUserFragment>
})
@@ -223,12 +200,7 @@ impl ContextualUserFragment for RenderedEnvironments {
let mut rendered = "\n".to_string();
if self.legacy_single {
if let Some(EnvironmentUpdate::Current(environment)) = self.updates.values().next() {
push_environment_values(
&mut rendered,
environment,
" ",
/*include_filesystem*/ false,
);
push_environment_values(&mut rendered, environment, " ");
}
} else if !self.updates.is_empty() {
rendered.push_str(" <environments>\n");
@@ -239,12 +211,7 @@ impl ContextualUserFragment for RenderedEnvironments {
push_xml_escaped_text(&mut rendered, id);
rendered.push('"');
rendered.push_str(">\n");
push_environment_values(
&mut rendered,
environment,
" ",
/*include_filesystem*/ true,
);
push_environment_values(&mut rendered, environment, " ");
rendered.push_str(" </environment>\n");
}
EnvironmentUpdate::Unavailable => {
@@ -281,12 +248,7 @@ impl ContextualUserFragment for RenderedEnvironments {
}
}
fn push_environment_values(
rendered: &mut String,
environment: &EnvironmentState,
indent: &str,
include_filesystem: bool,
) {
fn push_environment_values(rendered: &mut String, environment: &EnvironmentState, indent: &str) {
rendered.push_str(indent);
rendered.push_str("<cwd>");
push_xml_escaped_text(rendered, &environment.cwd.inferred_native_path_string());
@@ -301,11 +263,6 @@ fn push_environment_values(
push_xml_escaped_text(rendered, shell);
rendered.push_str("</shell>\n");
}
if include_filesystem && let Some(filesystem) = &environment.filesystem {
rendered.push_str(indent);
rendered.push_str(&filesystem.render());
rendered.push('\n');
}
}
fn push_optional_element(rendered: &mut String, name: &str, value: Option<&str>) {
@@ -326,7 +283,6 @@ struct EnvironmentState {
cwd: PathUri,
status: EnvironmentStatus,
shell: Option<String>,
filesystem: Option<FileSystemContext>,
}
#[derive(Default, Deserialize, Serialize)]
@@ -344,8 +300,6 @@ struct EnvironmentSnapshot {
cwd: String,
status: EnvironmentStatus,
shell: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
filesystem: Option<String>,
}
impl EnvironmentSnapshot {
@@ -357,7 +311,6 @@ impl EnvironmentSnapshot {
.as_ref()
.zip(other.shell.as_ref())
.is_none_or(|(current, previous)| current == previous)
&& self.filesystem == other.filesystem
}
}
@@ -381,10 +334,6 @@ fn environment_states(snapshot: &TurnEnvironmentSnapshot) -> BTreeMap<String, En
.shell
.as_ref()
.map(|shell| shell.name().to_string()),
filesystem: Some(FileSystemContext::from_permission_profile(
environment.permission_profile(),
environment.workspace_roots(),
)),
},
)
})
@@ -396,10 +345,6 @@ fn environment_states(snapshot: &TurnEnvironmentSnapshot) -> BTreeMap<String, En
cwd: environment.selection.cwd.clone(),
status: EnvironmentStatus::Starting,
shell: None,
filesystem: Some(FileSystemContext::from_permission_profile(
&environment.settings.sandbox.permission_profile,
&environment.selection.workspace_roots,
)),
});
}
environments
@@ -35,7 +35,6 @@ fn environment(id: &str, cwd: PathUri, shell: impl Into<String>) -> (String, Env
cwd,
status: EnvironmentStatus::Available,
shell: Some(shell.into()),
filesystem: None,
},
)
}
@@ -52,6 +51,7 @@ fn environment_state(
current_date,
timezone,
network,
filesystem: None,
subagents,
}
}
@@ -97,11 +97,7 @@ fn serialize_environment_context_with_foreign_windows_cwd() {
/*network*/ None,
/*subagents*/ None,
);
context
.environments
.get_mut("remote")
.expect("remote environment")
.filesystem = Some(FileSystemContext::from_permission_profile(
context.filesystem = Some(FileSystemContext::from_permission_profile(
&PermissionProfile::Disabled,
&[PathUri::parse("file:///D:/workspace").expect("Windows workspace root URI")],
));
@@ -195,11 +191,7 @@ fn serialize_environment_context_with_full_filesystem_profile() {
/*network*/ None,
/*subagents*/ None,
);
context
.environments
.get_mut("local")
.expect("local environment")
.filesystem = Some(FileSystemContext::from_permission_profile(
context.filesystem = Some(FileSystemContext::from_permission_profile(
&workspace_write_permission_profile_with_private_denials(),
&[
PathUri::from_abs_path(&repo),
@@ -56,59 +56,47 @@ fn snapshots() -> Result<()> {
.collect(),
..Default::default()
};
let mut before_turn_context_changes = EnvironmentsState {
let before_turn_context_changes = EnvironmentsState {
current_date: Some("2026-06-19".to_string()),
timezone: Some("UTC".to_string()),
network: Some(NetworkContext::new(
vec!["old.example.com".to_string()],
vec![],
)),
filesystem: Some(FileSystemContext::from_permission_profile(
&PermissionProfile::Disabled,
&[],
)),
..environments.clone()
};
before_turn_context_changes
.environments
.get_mut(LOCAL_ENVIRONMENT_ID)
.expect("local environment")
.filesystem = Some(FileSystemContext::from_permission_profile(
&PermissionProfile::Disabled,
&[],
));
let mut after_turn_context_changes = EnvironmentsState {
let after_turn_context_changes = EnvironmentsState {
current_date: Some("2026-06-20".to_string()),
timezone: Some("America/Los_Angeles".to_string()),
network: Some(NetworkContext::new(
vec!["new.example.com".to_string()],
vec!["blocked.example.com".to_string()],
)),
filesystem: Some(FileSystemContext::from_permission_profile(
&PermissionProfile::External {
network: NetworkSandboxPolicy::Restricted,
},
&[],
)),
..environments
};
after_turn_context_changes
.environments
.get_mut(LOCAL_ENVIRONMENT_ID)
.expect("local environment")
.filesystem = Some(FileSystemContext::from_permission_profile(
&PermissionProfile::External {
network: NetworkSandboxPolicy::Restricted,
},
&[],
));
let mut foreign_windows = EnvironmentsState {
let foreign_windows = EnvironmentsState {
environments: [(
"remote".to_string(),
available("file:///C:/windows", "powershell")?,
)]
.into_iter()
.collect(),
filesystem: Some(FileSystemContext::from_permission_profile(
&PermissionProfile::Disabled,
&[],
)),
..Default::default()
};
foreign_windows
.environments
.get_mut("remote")
.expect("remote environment")
.filesystem = Some(FileSystemContext::from_permission_profile(
&PermissionProfile::Disabled,
&[],
));
let unknown_shell = EnvironmentsState {
environments: [(
LOCAL_ENVIRONMENT_ID.to_string(),
@@ -116,7 +104,6 @@ fn snapshots() -> Result<()> {
cwd: PathUri::parse("file:///repo")?,
status: EnvironmentStatus::Available,
shell: None,
filesystem: None,
},
)]
.into_iter()
@@ -167,7 +154,6 @@ fn available(cwd: &str, shell: &str) -> Result<EnvironmentState> {
cwd: PathUri::parse(cwd)?,
status: EnvironmentStatus::Available,
shell: Some(shell.to_string()),
filesystem: None,
})
}
@@ -176,6 +162,5 @@ fn starting(cwd: &str) -> Result<EnvironmentState> {
cwd: PathUri::parse(cwd)?,
status: EnvironmentStatus::Starting,
shell: None,
filesystem: None,
})
}
@@ -255,7 +255,6 @@ fn reference_context_item() -> TurnContextItem {
permission_profile: None,
network: None,
file_system_sandbox_policy: None,
environment_sandboxes: None,
model: "gpt-test".to_string(),
comp_hash: None,
personality: None,
+23 -66
View File
@@ -1,4 +1,3 @@
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::collections::HashSet;
use std::fmt;
@@ -15,7 +14,6 @@ use codex_exec_server::ExecutorFileSystem;
use codex_protocol::protocol::EnvironmentConnectionEvent;
use codex_protocol::protocol::Event;
use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::TurnEnvironmentSandbox;
use codex_protocol::protocol::TurnEnvironmentSelection;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_path_uri::PathUri;
@@ -25,7 +23,6 @@ use futures::future::Shared;
use tokio_util::task::AbortOnDropHandle;
use crate::session::turn_context::TurnEnvironment;
use crate::session::turn_context::TurnEnvironmentSettings;
use crate::shell::Shell;
use crate::shell_snapshot::ShellSnapshot;
@@ -41,7 +38,7 @@ pub(crate) fn default_thread_environment_selections(
environment_id,
cwd: PathUri::from_abs_path(cwd),
workspace_roots: workspace_roots.iter().map(PathUri::from_abs_path).collect(),
sandbox: None,
permission_profile: None,
})
.collect()
}
@@ -52,7 +49,6 @@ type TurnEnvironmentResolution = Shared<BoxFuture<'static, TurnEnvironmentResult
#[derive(Clone)]
struct SelectedTurnEnvironment {
selection: TurnEnvironmentSelection,
settings: TurnEnvironmentSettings,
environment: Arc<Environment>,
// Selection clones share one listener; the final handle drop aborts it.
connection_events_task: Option<Arc<AbortOnDropHandle<()>>>,
@@ -62,7 +58,6 @@ struct SelectedTurnEnvironment {
#[derive(Clone)]
pub(crate) struct StartingTurnEnvironment {
pub(crate) selection: TurnEnvironmentSelection,
pub(crate) settings: TurnEnvironmentSettings,
resolution: TurnEnvironmentResolution,
}
@@ -108,13 +103,11 @@ impl ThreadEnvironments {
return None;
};
let selection = environment.selection();
let settings = environment.settings.clone();
let selected_environment = Arc::clone(&environment.environment);
let resolution: TurnEnvironmentResolution =
futures::future::ready(Ok(environment)).boxed().shared();
Some(SelectedTurnEnvironment {
selection,
settings,
environment: selected_environment,
connection_events_task: None,
resolution,
@@ -131,16 +124,7 @@ impl ThreadEnvironments {
}
}
#[cfg(test)]
pub(crate) fn update_selections(&self, environments: &[TurnEnvironmentSelection]) {
self.update_selections_with_settings(environments, &BTreeMap::new());
}
pub(crate) fn update_selections_with_settings(
&self,
environments: &[TurnEnvironmentSelection],
settings: &BTreeMap<String, TurnEnvironmentSettings>,
) {
let previous = self.environments.load();
let mut seen_environment_ids = HashSet::with_capacity(environments.len());
let mut next = Vec::with_capacity(environments.len());
@@ -148,14 +132,10 @@ impl ThreadEnvironments {
if !seen_environment_ids.insert(selected_environment.environment_id.as_str()) {
continue;
}
let environment_settings = settings
.get(&selected_environment.environment_id)
.cloned()
.unwrap_or_default();
if let Some(environment) = previous.iter().find(|environment| {
environment.selection == *selected_environment
&& environment.settings == environment_settings
}) && !matches!(environment.resolution.clone().now_or_never(), Some(Err(_)))
if let Some(environment) = previous
.iter()
.find(|environment| environment.selection == *selected_environment)
&& !matches!(environment.resolution.clone().now_or_never(), Some(Err(_)))
{
next.push(environment.clone());
continue;
@@ -185,7 +165,6 @@ impl ThreadEnvironments {
});
let (resolution_task, resolution) = Self::resolve_environment(
selected_environment.clone(),
environment_settings.clone(),
Arc::clone(&environment),
self.local_shell.clone(),
self.shell_snapshot.clone(),
@@ -195,7 +174,6 @@ impl ThreadEnvironments {
let resolution = resolution.boxed().shared();
next.push(SelectedTurnEnvironment {
selection: selected_environment.clone(),
settings: environment_settings,
environment,
connection_events_task,
resolution,
@@ -286,7 +264,6 @@ impl ThreadEnvironments {
fn resolve_environment(
selection: TurnEnvironmentSelection,
settings: TurnEnvironmentSettings,
environment: Arc<Environment>,
local_shell: Shell,
shell_snapshot: ShellSnapshot,
@@ -318,13 +295,12 @@ impl ThreadEnvironments {
} else {
Some(local_shell)
};
let mut turn_environment = TurnEnvironment::new_with_settings(
let mut turn_environment = TurnEnvironment::new_with_permission_profile(
selection.environment_id,
environment,
selection.cwd,
selection.workspace_roots,
selection.sandbox,
settings,
selection.permission_profile.unwrap_or_default(),
shell,
);
let task = shell_snapshot
@@ -351,7 +327,6 @@ impl ThreadEnvironments {
if let Some(environment) = TurnEnvironmentState::from_resolution(
StartingTurnEnvironment {
selection: environment.selection.clone(),
settings: environment.settings.clone(),
resolution: environment.resolution.clone(),
},
resolved,
@@ -428,24 +403,6 @@ impl TurnEnvironmentSnapshot {
})
}
pub(crate) fn environment_sandboxes(&self) -> BTreeMap<String, TurnEnvironmentSandbox> {
self.turn_environments()
.filter_map(|environment| {
environment
.selection()
.sandbox
.map(|sandbox| (environment.environment_id.clone(), sandbox))
})
.chain(self.starting().filter_map(|environment| {
environment
.selection
.sandbox
.clone()
.map(|sandbox| (environment.selection.environment_id.clone(), sandbox))
}))
.collect()
}
pub(crate) fn starting(&self) -> impl Iterator<Item = &StartingTurnEnvironment> {
self.environments.iter().filter_map(|environment| {
let TurnEnvironmentState::Starting(environment) = environment else {
@@ -636,7 +593,7 @@ mod tests {
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
cwd: cwd_uri.clone(),
workspace_roots: vec![cwd_uri],
sandbox: None,
permission_profile: None,
}]
);
}
@@ -667,13 +624,13 @@ url = "ws://127.0.0.1:8765"
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
cwd: cwd_uri.clone(),
workspace_roots: vec![cwd_uri.clone()],
sandbox: None,
permission_profile: None,
},
TurnEnvironmentSelection {
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
cwd: cwd_uri.clone(),
workspace_roots: vec![cwd_uri],
sandbox: None,
permission_profile: None,
},
]
);
@@ -708,7 +665,7 @@ url = "ws://127.0.0.1:8765"
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
cwd: PathUri::from_abs_path(&cwd),
workspace_roots: Vec::new(),
sandbox: None,
permission_profile: None,
}]);
let snapshot = turn_environments.snapshot().await;
@@ -730,7 +687,7 @@ url = "ws://127.0.0.1:8765"
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
cwd: cwd_uri.clone(),
workspace_roots: Vec::new(),
sandbox: None,
permission_profile: None,
};
let resolved = resolve_turn_environments(
@@ -741,7 +698,7 @@ url = "ws://127.0.0.1:8765"
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
cwd: cwd_uri.join("other").expect("other cwd URI"),
workspace_roots: Vec::new(),
sandbox: None,
permission_profile: None,
},
],
)
@@ -763,7 +720,7 @@ url = "ws://127.0.0.1:8765"
environment_id: "local".to_string(),
cwd: selected_cwd_uri,
workspace_roots: Vec::new(),
sandbox: None,
permission_profile: None,
}],
)
.await;
@@ -802,7 +759,7 @@ url = "ws://127.0.0.1:8765"
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
cwd: cwd_uri.clone(),
workspace_roots: Vec::new(),
sandbox: None,
permission_profile: None,
};
let resolved = resolve_turn_environments(
@@ -812,7 +769,7 @@ url = "ws://127.0.0.1:8765"
environment_id: "missing".to_string(),
cwd: cwd_uri,
workspace_roots: Vec::new(),
sandbox: None,
permission_profile: None,
},
local.clone(),
],
@@ -841,7 +798,7 @@ url = "ws://127.0.0.1:8765"
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
cwd: PathUri::from_abs_path(&AbsolutePathBuf::current_dir().expect("cwd")),
workspace_roots: Vec::new(),
sandbox: None,
permission_profile: None,
};
let environments = Arc::new(ThreadEnvironments::new(
manager,
@@ -890,13 +847,13 @@ url = "ws://127.0.0.1:8765"
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
cwd: cwd.clone(),
workspace_roots: Vec::new(),
sandbox: None,
permission_profile: None,
};
let local = TurnEnvironmentSelection {
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
cwd,
workspace_roots: Vec::new(),
sandbox: None,
permission_profile: None,
};
let turn_environments = ThreadEnvironments::new(
manager,
@@ -971,7 +928,7 @@ url = "ws://127.0.0.1:8765"
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
cwd: PathUri::from_abs_path(&AbsolutePathBuf::current_dir().expect("cwd")),
workspace_roots: Vec::new(),
sandbox: None,
permission_profile: None,
};
let environments = ThreadEnvironments::new(
Arc::clone(&manager),
@@ -1025,7 +982,7 @@ url = "ws://127.0.0.1:8765"
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
cwd: PathUri::from_abs_path(&cwd),
workspace_roots: Vec::new(),
sandbox: None,
permission_profile: None,
};
let (tx_event, rx_event) = async_channel::unbounded();
let environments = Arc::new(ThreadEnvironments::new(
@@ -1109,7 +1066,7 @@ url = "ws://127.0.0.1:8765"
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
cwd: PathUri::from_abs_path(&cwd),
workspace_roots: Vec::new(),
sandbox: None,
permission_profile: None,
};
let inherited_environment = Arc::new(
Environment::create_for_tests(Some("ws://127.0.0.1:8765".to_string()))
@@ -1163,7 +1120,7 @@ url = "ws://127.0.0.1:8765"
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
cwd: cwd_uri.clone(),
workspace_roots: Vec::new(),
sandbox: None,
permission_profile: None,
}],
)
.await;
+1 -1
View File
@@ -244,7 +244,7 @@ pub(crate) async fn build_guardian_prompt_items_with_parent_turn(
fn parent_turn_denied_reads_context(turn: &TurnContext) -> Option<String> {
#[allow(deprecated)]
let cwd = &turn.cwd;
let file_system_policy = turn.permission_profile().file_system_sandbox_policy();
let file_system_policy = turn.permission_profile.file_system_sandbox_policy();
let mut entries = file_system_policy
.get_unreadable_roots_with_cwd(cwd)
.into_iter()
+2 -2
View File
@@ -465,7 +465,7 @@ async fn build_guardian_prompt_includes_parent_turn_denied_reads() -> anyhow::Re
session.thread_id = fixed_guardian_parent_session_id();
let denied_root = test_path_buf("/repo/private").abs();
let denied_glob = test_path_buf("/repo/private/**").display().to_string();
turn.set_permission_profile_for_tests(PermissionProfile::from_runtime_permissions(
turn.permission_profile = PermissionProfile::from_runtime_permissions(
&FileSystemSandboxPolicy::restricted(vec![
FileSystemSandboxEntry {
path: FileSystemPath::Special {
@@ -487,7 +487,7 @@ async fn build_guardian_prompt_includes_parent_turn_denied_reads() -> anyhow::Re
},
]),
NetworkSandboxPolicy::Restricted,
));
);
let session = Arc::new(session);
let turn = Arc::new(turn);
seed_guardian_parent_history(&session, &turn).await;
+1 -2
View File
@@ -220,8 +220,7 @@ mod tests {
fn set_primary_environment_cwd(turn_context: &mut TurnContext, cwd: &Path) {
let cwd = AbsolutePathBuf::try_from(cwd).expect("absolute path");
turn_context
.set_permission_profile_for_tests(codex_protocol::models::PermissionProfile::Disabled);
turn_context.permission_profile = codex_protocol::models::PermissionProfile::Disabled;
let TurnEnvironmentState::Ready(primary) = &mut turn_context.environments.environments[0]
else {
panic!("expected ready primary environment");
+1 -1
View File
@@ -2996,7 +2996,7 @@ async fn full_access_mode_skips_mcp_tool_approval_for_all_approval_modes() {
.approval_policy
.set(AskForApproval::Never)
.expect("test setup should allow updating approval policy");
turn_context.set_permission_profile_for_tests(PermissionProfile::Disabled);
turn_context.permission_profile = PermissionProfile::Disabled;
let session = Arc::new(session);
let turn_context = Arc::new(turn_context);
+2 -15
View File
@@ -243,7 +243,6 @@ use self::turn::agent_message_text;
use self::turn::collect_explicit_app_ids_from_skill_items;
use self::turn::realtime_text_for_event;
use self::turn_context::TurnContext;
use self::turn_context::TurnEnvironmentRuntimeDefaults;
use self::turn_context::TurnSkillsContext;
#[cfg(test)]
mod rollout_reconstruction_tests;
@@ -668,12 +667,6 @@ impl Session {
approvals_reviewer: config.approvals_reviewer,
permission_profile_state: session_permission_profile_state_from_config(&config)?,
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
environment_runtime_defaults: TurnEnvironmentRuntimeDefaults {
windows_sandbox_private_desktop: config.permissions.windows_sandbox_private_desktop,
use_legacy_landlock: config.features.use_legacy_landlock(),
allow_login_shell: config.permissions.allow_login_shell,
shell_environment_policy: config.permissions.shell_environment_policy.clone(),
},
environments: TurnEnvironmentSelections::new(
config.cwd.clone(),
environment_selections,
@@ -1480,16 +1473,10 @@ impl Session {
let updated_permission_profile = updated.permission_profile();
let permission_profile_changed =
previous_permission_profile != updated_permission_profile;
let environment_settings = updated.environment_settings();
if updates.environments.is_some()
|| state.session_configuration.environment_settings() != environment_settings
{
if updates.environments.is_some() || permission_profile_changed {
self.services
.turn_environments
.update_selections_with_settings(
updated.environment_selections(),
&environment_settings,
);
.update_selections(&updated.resolved_environment_selections());
}
state.session_configuration = updated;
(previous_config, new_config, permission_profile_changed)
+6 -4
View File
@@ -49,7 +49,8 @@ pub(super) async fn spawn_review_thread(
per_turn_config.model = Some(model.clone());
per_turn_config.features = review_features.clone();
per_turn_config.permissions.shell_environment_policy = parent_turn_context
.primary_environment_settings()
.config
.permissions
.shell_environment_policy
.clone();
per_turn_config.codex_linux_sandbox_exe =
@@ -97,8 +98,8 @@ pub(super) async fn spawn_review_thread(
review_turn_id.clone(),
#[allow(deprecated)]
parent_turn_context.cwd.clone(),
&parent_turn_context.permission_profile(),
parent_turn_context.windows_sandbox_level(),
&parent_turn_context.permission_profile,
parent_turn_context.windows_sandbox_level,
parent_turn_context.network.is_some(),
));
@@ -136,8 +137,9 @@ pub(super) async fn spawn_review_thread(
multi_agent_version: MultiAgentVersion::Disabled,
personality: parent_turn_context.personality,
approval_policy: parent_turn_context.approval_policy.clone(),
default_environment_settings: parent_turn_context.default_environment_settings.clone(),
permission_profile: parent_turn_context.permission_profile(),
network: parent_turn_context.network.clone(),
windows_sandbox_level: parent_turn_context.windows_sandbox_level,
#[allow(deprecated)]
cwd: parent_turn_context.cwd.clone(),
final_output_json_schema: None,
@@ -191,7 +191,6 @@ async fn record_initial_history_resumed_bare_turn_context_does_not_hydrate_previ
permission_profile: None,
network: None,
file_system_sandbox_policy: None,
environment_sandboxes: None,
model: previous_model.to_string(),
comp_hash: None,
personality: turn_context.personality,
@@ -239,7 +238,6 @@ async fn record_initial_history_resumed_hydrates_previous_turn_settings_from_lif
permission_profile: None,
network: None,
file_system_sandbox_policy: None,
environment_sandboxes: None,
model: previous_model.to_string(),
comp_hash: Some("comp-hash-a".to_string()),
personality: turn_context.personality,
@@ -1303,7 +1301,6 @@ async fn record_initial_history_resumed_turn_context_after_compaction_reestablis
permission_profile: None,
network: None,
file_system_sandbox_policy: None,
environment_sandboxes: None,
model: previous_model.to_string(),
comp_hash: None,
personality: turn_context.personality,
@@ -1393,7 +1390,6 @@ async fn record_initial_history_resumed_turn_context_after_compaction_reestablis
permission_profile: None,
network: None,
file_system_sandbox_policy: None,
environment_sandboxes: None,
model: previous_model.to_string(),
comp_hash: None,
personality: turn_context.personality,
@@ -1426,7 +1422,6 @@ async fn record_initial_history_resumed_aborted_turn_without_id_clears_active_tu
permission_profile: None,
network: None,
file_system_sandbox_policy: None,
environment_sandboxes: None,
model: previous_model.to_string(),
comp_hash: None,
personality: turn_context.personality,
@@ -1557,7 +1552,6 @@ async fn record_initial_history_resumed_unmatched_abort_preserves_active_turn_fo
permission_profile: None,
network: None,
file_system_sandbox_policy: None,
environment_sandboxes: None,
model: current_model.to_string(),
comp_hash: None,
personality: turn_context.personality,
@@ -1685,7 +1679,6 @@ async fn record_initial_history_resumed_trailing_incomplete_turn_compaction_clea
permission_profile: None,
network: None,
file_system_sandbox_policy: None,
environment_sandboxes: None,
model: previous_model.to_string(),
comp_hash: None,
personality: turn_context.personality,
@@ -1856,7 +1849,6 @@ async fn record_initial_history_resumed_replaced_incomplete_compacted_turn_clear
permission_profile: None,
network: None,
file_system_sandbox_policy: None,
environment_sandboxes: None,
model: previous_model.to_string(),
comp_hash: None,
personality: turn_context.personality,
+20 -36
View File
@@ -4,8 +4,6 @@ use crate::agents_md_manager::AgentsMdManager;
use crate::config::ConstraintError;
use crate::environment_selection::ThreadEnvironments;
use crate::environment_selection::TurnEnvironmentSnapshot;
use crate::session::turn_context::TurnEnvironmentRuntimeDefaults;
use crate::session::turn_context::TurnEnvironmentSettings;
use crate::shell_snapshot::ShellSnapshot;
use crate::skills::SkillError;
use crate::state::ActiveTurn;
@@ -20,9 +18,7 @@ use codex_protocol::permissions::FileSystemSpecialPath;
use codex_protocol::protocol::MultiAgentVersion;
use codex_protocol::protocol::ThreadHistoryMode;
use codex_protocol::protocol::ThreadSource;
use codex_protocol::protocol::TurnEnvironmentSandbox;
use codex_protocol::protocol::TurnEnvironmentSelections;
use std::collections::BTreeMap;
use std::sync::OnceLock;
use tokio::sync::Semaphore;
@@ -80,7 +76,6 @@ pub(crate) struct SessionConfiguration {
/// the methods below instead of mutating the fields independently.
pub(super) permission_profile_state: PermissionProfileState,
pub(super) windows_sandbox_level: WindowsSandboxLevel,
pub(super) environment_runtime_defaults: TurnEnvironmentRuntimeDefaults,
/// Sticky thread-level environment selections plus the legacy cwd used
/// when a turn does not select an environment.
@@ -121,38 +116,29 @@ impl SessionConfiguration {
&self.environments.environments
}
pub(super) fn environment_settings(&self) -> BTreeMap<String, TurnEnvironmentSettings> {
let default_sandbox = TurnEnvironmentSandbox {
permission_profile: self.permission_profile(),
active_permission_profile: self.active_permission_profile(),
windows_sandbox_level: self.windows_sandbox_level,
windows_sandbox_private_desktop: self
.environment_runtime_defaults
.windows_sandbox_private_desktop,
use_legacy_landlock: self.environment_runtime_defaults.use_legacy_landlock,
};
let mut settings = BTreeMap::new();
for environment in self.environment_selections() {
settings
.entry(environment.environment_id.clone())
.or_insert_with(|| {
let sandbox = environment
.sandbox
.as_ref()
.cloned()
.unwrap_or_else(|| default_sandbox.clone());
self.environment_runtime_defaults.settings(sandbox)
});
}
settings
pub(super) fn resolved_environment_selections(&self) -> Vec<TurnEnvironmentSelection> {
let default_permission_profile = self.permission_profile();
self.environment_selections()
.iter()
.cloned()
.map(|mut environment| {
if environment.permission_profile.is_none() {
environment.permission_profile = Some(default_permission_profile.clone());
}
environment
})
.collect()
}
pub(super) fn uses_managed_network_proxy(&self) -> bool {
!matches!(self.permission_profile(), PermissionProfile::Disabled)
|| self.environment_selections().iter().any(|environment| {
environment.sandbox.as_ref().is_some_and(|sandbox| {
!matches!(sandbox.permission_profile, PermissionProfile::Disabled)
})
environment
.permission_profile
.as_ref()
.is_some_and(|permission_profile| {
!matches!(permission_profile, PermissionProfile::Disabled)
})
})
}
@@ -953,10 +939,8 @@ impl Session {
inherited_environments.unwrap_or_default(),
config.features.enabled(Feature::DeferredExecutor),
));
turn_environments.update_selections_with_settings(
session_configuration.environment_selections(),
&session_configuration.environment_settings(),
);
turn_environments
.update_selections(&session_configuration.resolved_environment_selections());
let resolved_environments = turn_environments.snapshot().await;
let agents_md_manager = Arc::new(AgentsMdManager::new(user_instructions));
let plugin_skill_warmup = warm_plugins_and_skills_for_session_init(
+15 -27
View File
@@ -1037,8 +1037,8 @@ async fn danger_full_access_turns_do_not_expose_managed_network_proxy() -> anyho
#[tokio::test]
async fn danger_full_access_tool_attempts_do_not_enforce_managed_network() -> anyhow::Result<()> {
struct ProbeToolRuntime {
turn_environment: TurnEnvironment,
enforce_managed_network: Vec<bool>,
turn_environment: TurnEnvironment,
}
impl crate::tools::sandboxing::Approvable<()> for ProbeToolRuntime {
@@ -1145,12 +1145,13 @@ async fn danger_full_access_tool_attempts_do_not_enforce_managed_network() -> an
let mut orchestrator = crate::tools::orchestrator::ToolOrchestrator::new();
let mut tool = ProbeToolRuntime {
enforce_managed_network: Vec::new(),
turn_environment: turn
.environments
.primary()
.expect("primary environment")
.turn_environments()
.next()
.expect("default environment")
.clone(),
enforce_managed_network: Vec::new(),
};
let tool_ctx = crate::tools::sandboxing::ToolCtx {
session: Arc::clone(&session),
@@ -3078,7 +3079,6 @@ async fn record_initial_history_forked_hydrates_previous_turn_settings() {
permission_profile: None,
network: None,
file_system_sandbox_policy: None,
environment_sandboxes: None,
model: previous_model.to_string(),
comp_hash: None,
personality: turn_context.personality,
@@ -3748,7 +3748,6 @@ async fn set_rate_limits_retains_previous_credits() {
approvals_reviewer: config.approvals_reviewer,
permission_profile_state: config.permissions.permission_profile_state().clone(),
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
environment_runtime_defaults: Default::default(),
environments: TurnEnvironmentSelections::new(config.cwd.clone(), Vec::new()),
codex_home: config.codex_home.clone(),
thread_name: None,
@@ -3858,7 +3857,6 @@ async fn set_rate_limits_updates_plan_type_when_present() {
approvals_reviewer: config.approvals_reviewer,
permission_profile_state: config.permissions.permission_profile_state().clone(),
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
environment_runtime_defaults: Default::default(),
environments: TurnEnvironmentSelections::new(config.cwd.clone(), Vec::new()),
codex_home: config.codex_home.clone(),
thread_name: None,
@@ -4399,7 +4397,6 @@ pub(crate) async fn make_session_configuration_for_tests() -> SessionConfigurati
approvals_reviewer: config.approvals_reviewer,
permission_profile_state: config.permissions.permission_profile_state().clone(),
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
environment_runtime_defaults: Default::default(),
environments: TurnEnvironmentSelections::new(config.cwd.clone(), Vec::new()),
codex_home: config.codex_home.clone(),
thread_name: None,
@@ -5132,7 +5129,6 @@ async fn session_new_fails_when_zsh_fork_enabled_without_packaged_zsh() {
approvals_reviewer: config.approvals_reviewer,
permission_profile_state: config.permissions.permission_profile_state().clone(),
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
environment_runtime_defaults: Default::default(),
environments: TurnEnvironmentSelections::new(config.cwd.clone(), Vec::new()),
codex_home: config.codex_home.clone(),
thread_name: None,
@@ -5264,7 +5260,6 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
approvals_reviewer: config.approvals_reviewer,
permission_profile_state: config.permissions.permission_profile_state().clone(),
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
environment_runtime_defaults: Default::default(),
environments: TurnEnvironmentSelections::new(config.cwd.clone(), default_environments),
codex_home: config.codex_home.clone(),
thread_name: None,
@@ -5521,7 +5516,6 @@ async fn make_session_with_config_and_rx(
approvals_reviewer: config.approvals_reviewer,
permission_profile_state: config.permissions.permission_profile_state().clone(),
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
environment_runtime_defaults: Default::default(),
environments: TurnEnvironmentSelections::new(config.cwd.clone(), default_environments),
codex_home: config.codex_home.clone(),
thread_name: None,
@@ -5629,7 +5623,6 @@ async fn make_session_with_history_source_and_agent_control_and_rx(
approvals_reviewer: config.approvals_reviewer,
permission_profile_state: config.permissions.permission_profile_state().clone(),
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
environment_runtime_defaults: Default::default(),
environments: TurnEnvironmentSelections::new(config.cwd.clone(), default_environments),
codex_home: config.codex_home.clone(),
thread_name: None,
@@ -7426,7 +7419,6 @@ where
approvals_reviewer: config.approvals_reviewer,
permission_profile_state: config.permissions.permission_profile_state().clone(),
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
environment_runtime_defaults: Default::default(),
environments: TurnEnvironmentSelections::new(config.cwd.clone(), default_environments),
codex_home: config.codex_home.clone(),
thread_name: None,
@@ -8840,12 +8832,10 @@ async fn turn_context_item_omits_legacy_equivalent_file_system_sandbox_policy()
async fn turn_context_item_stores_split_file_system_sandbox_policy_when_different() {
let (_session, mut turn_context) = make_session_and_context().await;
let file_system_sandbox_policy = file_system_policy_with_unreadable_glob(&turn_context);
turn_context.set_permission_profile_for_tests(
PermissionProfile::from_runtime_permissions_with_enforcement(
turn_context.permission_profile().enforcement(),
&file_system_sandbox_policy,
turn_context.network_sandbox_policy(),
),
turn_context.permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
turn_context.permission_profile.enforcement(),
&file_system_sandbox_policy,
turn_context.network_sandbox_policy(),
);
let item = turn_context.to_turn_context_item();
@@ -9000,12 +8990,10 @@ async fn record_context_updates_and_set_reference_context_item_persists_split_fi
{
let (mut session, mut turn_context) = make_session_and_context().await;
let file_system_sandbox_policy = file_system_policy_with_unreadable_glob(&turn_context);
turn_context.set_permission_profile_for_tests(
PermissionProfile::from_runtime_permissions_with_enforcement(
turn_context.permission_profile().enforcement(),
&file_system_sandbox_policy,
turn_context.network_sandbox_policy(),
),
turn_context.permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
turn_context.permission_profile.enforcement(),
&file_system_sandbox_policy,
turn_context.network_sandbox_policy(),
);
let rollout_path = attach_thread_persistence(&mut session).await;
@@ -10766,7 +10754,7 @@ async fn rejects_escalated_permissions_when_policy_not_on_request() {
// command. Force DangerFullAccess so this check stays focused on approval
// policy rather than platform-specific sandbox behavior.
let turn_context_mut = Arc::get_mut(&mut turn_context).expect("unique thread settings Arc");
turn_context_mut.set_permission_profile_for_tests(PermissionProfile::Disabled);
turn_context_mut.permission_profile = PermissionProfile::Disabled;
let command = session.user_shell().derive_exec_args(
command_script,
@@ -10779,7 +10767,7 @@ async fn rejects_escalated_permissions_when_policy_not_on_request() {
command: &command,
approval_policy: turn_context.approval_policy.value(),
permission_profile: turn_context.permission_profile(),
windows_sandbox_level: turn_context.windows_sandbox_level(),
windows_sandbox_level: turn_context.windows_sandbox_level,
sandbox_permissions: SandboxPermissions::UseDefault,
prefix_rule: None,
})
@@ -301,8 +301,7 @@ async fn guardian_allows_shell_command_additional_permissions_requests_past_poli
.features
.enable(Feature::ExecPermissionApprovals)
.expect("test setup should allow enabling request permissions");
turn_context_raw
.set_permission_profile_for_tests(codex_protocol::models::PermissionProfile::Disabled);
turn_context_raw.permission_profile = codex_protocol::models::PermissionProfile::Disabled;
let mut config = (*turn_context_raw.config).clone();
config
.permissions
@@ -417,8 +416,7 @@ async fn strict_auto_review_turn_grant_forces_guardian_for_shell_command_policy_
.approval_policy
.set(AskForApproval::Never)
.expect("test setup should allow updating approval policy");
turn_context_raw
.set_permission_profile_for_tests(codex_protocol::models::PermissionProfile::Disabled);
turn_context_raw.permission_profile = codex_protocol::models::PermissionProfile::Disabled;
let mut config = (*turn_context_raw.config).clone();
config
.permissions
+65 -176
View File
@@ -7,13 +7,11 @@ use codex_model_provider::SharedModelProvider;
use codex_model_provider::create_model_provider;
use codex_protocol::SessionId;
use codex_protocol::ThreadId;
use codex_protocol::config_types::ShellEnvironmentPolicy;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::openai_models::ModelInfo;
use codex_protocol::protocol::ErrorEvent;
use codex_protocol::protocol::MultiAgentVersion;
use codex_protocol::protocol::ThreadHistoryMode;
use codex_protocol::protocol::TurnEnvironmentSandbox;
use codex_protocol::protocol::TurnEnvironmentSelection;
use codex_sandboxing::compatibility_sandbox_policy_for_permission_profile;
use codex_sandboxing::policy_transforms::effective_file_system_sandbox_policy;
@@ -43,60 +41,13 @@ impl TurnSkillsContext {
pub(crate) type ShellSnapshotTask = Shared<BoxFuture<'static, Option<Arc<ShellSnapshotFile>>>>;
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct TurnEnvironmentSettings {
pub(crate) sandbox: TurnEnvironmentSandbox,
pub(crate) allow_login_shell: bool,
pub(crate) shell_environment_policy: ShellEnvironmentPolicy,
}
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct TurnEnvironmentRuntimeDefaults {
pub(crate) windows_sandbox_private_desktop: bool,
pub(crate) use_legacy_landlock: bool,
pub(crate) allow_login_shell: bool,
pub(crate) shell_environment_policy: ShellEnvironmentPolicy,
}
impl Default for TurnEnvironmentRuntimeDefaults {
fn default() -> Self {
Self {
windows_sandbox_private_desktop: false,
use_legacy_landlock: false,
allow_login_shell: true,
shell_environment_policy: ShellEnvironmentPolicy::default(),
}
}
}
impl TurnEnvironmentRuntimeDefaults {
pub(crate) fn settings(&self, sandbox: TurnEnvironmentSandbox) -> TurnEnvironmentSettings {
TurnEnvironmentSettings {
sandbox,
allow_login_shell: self.allow_login_shell,
shell_environment_policy: self.shell_environment_policy.clone(),
}
}
}
impl Default for TurnEnvironmentSettings {
fn default() -> Self {
Self {
sandbox: TurnEnvironmentSandbox::default(),
allow_login_shell: true,
shell_environment_policy: ShellEnvironmentPolicy::default(),
}
}
}
#[derive(Clone)]
pub(crate) struct TurnEnvironment {
pub(crate) environment_id: String,
pub(crate) environment: Arc<Environment>,
cwd: PathUri,
workspace_roots: Vec<PathUri>,
sandbox_override: Option<TurnEnvironmentSandbox>,
pub(crate) settings: TurnEnvironmentSettings,
permission_profile: PermissionProfile,
pub(crate) shell: Option<shell::Shell>,
pub(crate) shell_snapshot: ShellSnapshotTask,
}
@@ -110,24 +61,22 @@ impl TurnEnvironment {
workspace_roots: Vec<PathUri>,
shell: Option<shell::Shell>,
) -> Self {
Self::new_with_settings(
Self::new_with_permission_profile(
environment_id,
environment,
cwd,
workspace_roots,
/*sandbox_override*/ None,
TurnEnvironmentSettings::default(),
PermissionProfile::default(),
shell,
)
}
pub(crate) fn new_with_settings(
pub(crate) fn new_with_permission_profile(
environment_id: String,
environment: Arc<Environment>,
cwd: PathUri,
workspace_roots: Vec<PathUri>,
sandbox_override: Option<TurnEnvironmentSandbox>,
settings: TurnEnvironmentSettings,
permission_profile: PermissionProfile,
shell: Option<shell::Shell>,
) -> Self {
Self {
@@ -135,8 +84,7 @@ impl TurnEnvironment {
environment,
cwd,
workspace_roots,
sandbox_override,
settings,
permission_profile,
shell,
shell_snapshot: futures::future::ready(None).boxed().shared(),
}
@@ -161,53 +109,19 @@ impl TurnEnvironment {
}
pub(crate) fn permission_profile(&self) -> &PermissionProfile {
&self.settings.sandbox.permission_profile
}
pub(crate) fn active_permission_profile(&self) -> Option<ActivePermissionProfile> {
self.settings.sandbox.active_permission_profile.clone()
&self.permission_profile
}
pub(crate) fn file_system_sandbox_policy(&self) -> FileSystemSandboxPolicy {
self.permission_profile().file_system_sandbox_policy()
self.permission_profile.file_system_sandbox_policy()
}
pub(crate) fn network_sandbox_policy(&self) -> NetworkSandboxPolicy {
self.permission_profile().network_sandbox_policy()
self.permission_profile.network_sandbox_policy()
}
pub(crate) fn uses_managed_network_proxy(&self) -> bool {
!matches!(self.permission_profile(), PermissionProfile::Disabled)
}
pub(crate) fn file_system_sandbox_context(
&self,
additional_permissions: Option<AdditionalPermissionProfile>,
) -> FileSystemSandboxContext {
let permission_profile = self.permission_profile();
let (base_file_system_sandbox_policy, base_network_sandbox_policy) =
permission_profile.to_runtime_permissions();
let file_system_sandbox_policy = effective_file_system_sandbox_policy(
&base_file_system_sandbox_policy,
additional_permissions.as_ref(),
);
let network_sandbox_policy = effective_network_sandbox_policy(
base_network_sandbox_policy,
additional_permissions.as_ref(),
);
let permissions = PermissionProfile::from_runtime_permissions_with_enforcement(
permission_profile.enforcement(),
&file_system_sandbox_policy,
network_sandbox_policy,
);
FileSystemSandboxContext {
permissions: permissions.into(),
cwd: Some(self.cwd().clone()),
workspace_roots: self.workspace_roots().to_vec(),
windows_sandbox_level: self.settings.sandbox.windows_sandbox_level,
windows_sandbox_private_desktop: self.settings.sandbox.windows_sandbox_private_desktop,
use_legacy_landlock: self.settings.sandbox.use_legacy_landlock,
}
!matches!(self.permission_profile, PermissionProfile::Disabled)
}
pub(crate) fn selection(&self) -> TurnEnvironmentSelection {
@@ -215,7 +129,7 @@ impl TurnEnvironment {
environment_id: self.environment_id.clone(),
cwd: self.cwd.clone(),
workspace_roots: self.workspace_roots.clone(),
sandbox: self.sandbox_override.clone(),
permission_profile: Some(self.permission_profile.clone()),
}
}
}
@@ -227,8 +141,7 @@ impl std::fmt::Debug for TurnEnvironment {
.field("environment", &self.environment)
.field("cwd", &self.cwd)
.field("workspace_roots", &self.workspace_roots)
.field("sandbox_override", &self.sandbox_override)
.field("settings", &self.settings)
.field("permission_profile", &self.permission_profile)
.field("shell", &self.shell)
.finish_non_exhaustive()
}
@@ -266,8 +179,9 @@ pub struct TurnContext {
pub(crate) multi_agent_version: MultiAgentVersion,
pub(crate) personality: Option<Personality>,
pub(crate) approval_policy: Constrained<AskForApproval>,
pub(crate) default_environment_settings: TurnEnvironmentSettings,
pub(crate) permission_profile: PermissionProfile,
pub(crate) network: Option<NetworkProxy>,
pub(crate) windows_sandbox_level: WindowsSandboxLevel,
pub(crate) available_models: Vec<ModelPreset>,
pub(crate) unified_exec_shell_mode: UnifiedExecShellMode,
pub(crate) final_output_json_schema: Option<Value>,
@@ -303,55 +217,21 @@ impl TurnContext {
}
}
pub(crate) fn primary_environment_settings(&self) -> &TurnEnvironmentSettings {
self.environments
.primary()
.map(|environment| &environment.settings)
.unwrap_or(&self.default_environment_settings)
}
pub(crate) fn permission_profile(&self) -> PermissionProfile {
self.primary_environment_settings()
.sandbox
.permission_profile
.clone()
}
#[cfg(test)]
pub(crate) fn set_permission_profile_for_tests(
&mut self,
permission_profile: PermissionProfile,
) {
self.default_environment_settings.sandbox.permission_profile = permission_profile.clone();
for environment in &mut self.environments.environments {
match environment {
crate::environment_selection::TurnEnvironmentState::Ready(environment) => {
environment.settings.sandbox.permission_profile = permission_profile.clone();
}
crate::environment_selection::TurnEnvironmentState::Starting(environment) => {
environment.settings.sandbox.permission_profile = permission_profile.clone();
}
}
}
}
pub(crate) fn windows_sandbox_level(&self) -> WindowsSandboxLevel {
self.primary_environment_settings()
.sandbox
.windows_sandbox_level
self.permission_profile.clone()
}
pub(crate) fn file_system_sandbox_policy(&self) -> FileSystemSandboxPolicy {
self.permission_profile().file_system_sandbox_policy()
self.permission_profile.file_system_sandbox_policy()
}
pub(crate) fn network_sandbox_policy(&self) -> NetworkSandboxPolicy {
self.permission_profile().network_sandbox_policy()
self.permission_profile.network_sandbox_policy()
}
pub(crate) fn sandbox_policy(&self) -> SandboxPolicy {
compatibility_sandbox_policy_for_permission_profile(
&self.permission_profile(),
&self.permission_profile,
#[allow(deprecated)]
&self.cwd,
)
@@ -461,8 +341,9 @@ impl TurnContext {
multi_agent_version: self.multi_agent_version,
personality: self.personality,
approval_policy: self.approval_policy.clone(),
default_environment_settings: self.default_environment_settings.clone(),
permission_profile: self.permission_profile.clone(),
network: self.network.clone(),
windows_sandbox_level: self.windows_sandbox_level,
available_models,
unified_exec_shell_mode: self.unified_exec_shell_mode.clone(),
final_output_json_schema: self.final_output_json_schema.clone(),
@@ -481,6 +362,40 @@ impl TurnContext {
}
}
pub(crate) fn file_system_sandbox_context(
&self,
additional_permissions: Option<AdditionalPermissionProfile>,
environment: &TurnEnvironment,
) -> FileSystemSandboxContext {
let permission_profile = environment.permission_profile();
let (base_file_system_sandbox_policy, base_network_sandbox_policy) =
permission_profile.to_runtime_permissions();
let file_system_sandbox_policy = effective_file_system_sandbox_policy(
&base_file_system_sandbox_policy,
additional_permissions.as_ref(),
);
let network_sandbox_policy = effective_network_sandbox_policy(
base_network_sandbox_policy,
additional_permissions.as_ref(),
);
let permissions = PermissionProfile::from_runtime_permissions_with_enforcement(
permission_profile.enforcement(),
&file_system_sandbox_policy,
network_sandbox_policy,
);
FileSystemSandboxContext {
permissions: permissions.into(),
cwd: Some(environment.cwd().clone()),
workspace_roots: environment.workspace_roots().to_vec(),
windows_sandbox_level: self.windows_sandbox_level,
windows_sandbox_private_desktop: self
.config
.permissions
.windows_sandbox_private_desktop,
use_legacy_landlock: self.config.features.use_legacy_landlock(),
}
}
fn non_legacy_file_system_sandbox_policy(&self) -> Option<FileSystemSandboxPolicy> {
// Omit the derived split filesystem policy when it is equivalent to
// the legacy sandbox policy. This keeps turn-context payloads stable
@@ -499,7 +414,6 @@ impl TurnContext {
pub(crate) fn to_turn_context_item(&self) -> TurnContextItem {
let workspace_roots = self.config.effective_workspace_roots();
let environment_sandboxes = self.environments.environment_sandboxes();
#[allow(deprecated)]
let cwd = self.cwd.clone();
TurnContextItem {
@@ -514,8 +428,6 @@ impl TurnContext {
permission_profile: Some(self.permission_profile()),
network: self.turn_context_network_item(),
file_system_sandbox_policy: self.non_legacy_file_system_sandbox_policy(),
environment_sandboxes: (!environment_sandboxes.is_empty())
.then_some(environment_sandboxes),
model: self.model_info.slug.clone(),
comp_hash: self.model_info.comp_hash.clone(),
personality: self.personality,
@@ -660,28 +572,7 @@ impl Session {
per_turn_config.features.enabled(Feature::FastMode),
&model_info,
);
let default_environment_settings = session_configuration
.environment_runtime_defaults
.settings(TurnEnvironmentSandbox {
permission_profile: session_configuration.permission_profile(),
active_permission_profile: session_configuration.active_permission_profile(),
windows_sandbox_level: session_configuration.windows_sandbox_level,
windows_sandbox_private_desktop: session_configuration
.environment_runtime_defaults
.windows_sandbox_private_desktop,
use_legacy_landlock: session_configuration
.environment_runtime_defaults
.use_legacy_landlock,
});
let primary_environment_settings = environments
.primary()
.map(|environment| &environment.settings)
.unwrap_or(&default_environment_settings);
let permission_profile = primary_environment_settings
.sandbox
.permission_profile
.clone();
let windows_sandbox_level = primary_environment_settings.sandbox.windows_sandbox_level;
let permission_profile = per_turn_config.permissions.effective_permission_profile();
let per_turn_config = Arc::new(per_turn_config);
let turn_metadata_state = Arc::new(TurnMetadataState::new(
session_id.to_string(),
@@ -693,7 +584,7 @@ impl Session {
sub_id.clone(),
cwd.clone(),
&permission_profile,
windows_sandbox_level,
session_configuration.windows_sandbox_level,
network.is_some(),
));
let (current_date, timezone) = local_time_context();
@@ -729,8 +620,9 @@ impl Session {
multi_agent_version,
personality: session_configuration.personality,
approval_policy: session_configuration.approval_policy.clone(),
default_environment_settings,
permission_profile,
network,
windows_sandbox_level: session_configuration.windows_sandbox_level,
available_models,
unified_exec_shell_mode,
final_output_json_schema: None,
@@ -765,17 +657,10 @@ impl Session {
});
let new_config = notify_config_contributors
.then(|| Self::build_effective_session_config(&next));
let environment_settings = next.environment_settings();
if updates.environments.is_some()
|| state.session_configuration.environment_settings()
!= environment_settings
{
if updates.environments.is_some() || permission_profile_changed {
self.services
.turn_environments
.update_selections_with_settings(
next.environment_selections(),
&environment_settings,
);
.update_selections(&next.resolved_environment_selections());
}
state.session_configuration = next.clone();
Ok((
@@ -934,7 +819,11 @@ impl Session {
.network_proxy
.load_full()
.as_ref()
.map(|started_proxy| started_proxy.proxy()),
.and_then(|started_proxy| {
session_configuration
.uses_managed_network_proxy()
.then(|| started_proxy.proxy())
}),
turn_environments,
cwd,
sub_id,
+11 -12
View File
@@ -157,7 +157,7 @@ pub(crate) async fn execute_user_shell_command(
};
let shell_snapshot_location = turn_environment.shell_snapshot(&cwd);
let mut exec_env_map = create_env(
&turn_environment.settings.shell_environment_policy,
&turn_context.config.permissions.shell_environment_policy,
Some(session.thread_id),
);
if exec_env_map.contains_key(PROXY_ACTIVE_ENV_KEY) {
@@ -167,7 +167,11 @@ pub(crate) async fn execute_user_shell_command(
&display_command,
environment_shell,
shell_snapshot_location.as_ref(),
&turn_environment.settings.shell_environment_policy.r#set,
&turn_context
.config
.permissions
.shell_environment_policy
.r#set,
&mut exec_env_map,
);
@@ -197,11 +201,6 @@ pub(crate) async fn execute_user_shell_command(
)
.await;
let windows_sandbox_workspace_roots = turn_environment
.workspace_roots()
.iter()
.filter_map(|root| root.to_abs_path().ok())
.collect();
let permission_profile = PermissionProfile::Disabled;
let exec_env = ExecRequest {
command: exec_command.clone(),
@@ -218,11 +217,11 @@ pub(crate) async fn execute_user_shell_command(
capture_policy: ExecCapturePolicy::ShellTool,
sandbox: SandboxType::None,
windows_sandbox_policy_cwd: cwd.clone().into(),
windows_sandbox_workspace_roots,
windows_sandbox_level: turn_environment.settings.sandbox.windows_sandbox_level,
windows_sandbox_private_desktop: turn_environment
.settings
.sandbox
windows_sandbox_workspace_roots: turn_context.config.effective_workspace_roots(),
windows_sandbox_level: turn_context.windows_sandbox_level,
windows_sandbox_private_desktop: turn_context
.config
.permissions
.windows_sandbox_private_desktop,
permission_profile: permission_profile.clone(),
file_system_sandbox_policy: permission_profile.file_system_sandbox_policy(),
+1 -1
View File
@@ -860,7 +860,7 @@ async fn resume_and_fork_do_not_restore_thread_environments_from_rollout() {
environment_id: "local".to_string(),
cwd: PathUri::from_abs_path(&selected_cwd),
workspace_roots: Vec::new(),
sandbox: None,
permission_profile: None,
}];
let default_cwd = config.cwd.clone();
let mut source_config = config.clone();
@@ -383,8 +383,8 @@ impl ApplyPatchHandler {
));
};
let fs = turn_environment.environment.get_filesystem();
let sandbox =
turn_environment.file_system_sandbox_context(/*additional_permissions*/ None);
let sandbox = turn
.file_system_sandbox_context(/*additional_permissions*/ None, turn_environment);
match codex_apply_patch::verify_apply_patch_args(
args,
turn_environment.cwd(),
@@ -558,7 +558,7 @@ pub(crate) async fn intercept_apply_patch(
tool_name: &str,
) -> Result<Option<FunctionToolOutput>, FunctionCallError> {
let sandbox =
turn_environment.file_system_sandbox_context(/*additional_permissions*/ None);
turn.file_system_sandbox_context(/*additional_permissions*/ None, &turn_environment);
match codex_apply_patch::maybe_parse_apply_patch_verified(command, cwd, fs, Some(&sandbox))
.await
{
@@ -140,8 +140,9 @@ async fn to_extension_call(invocation: &ToolInvocation) -> ExtensionToolCall {
)
.await
.additional_permissions;
let file_system_sandbox_context =
environment.file_system_sandbox_context(additional_permissions);
let file_system_sandbox_context = invocation
.turn
.file_system_sandbox_context(additional_permissions, environment);
environments.push(ToolEnvironment {
environment_id: environment.environment_id.clone(),
cwd: native_cwd,
@@ -2296,7 +2296,7 @@ async fn spawn_agent_reapplies_runtime_sandbox_after_role_config() {
let mut config = (*turn.config).clone();
config.approvals_reviewer = ApprovalsReviewer::AutoReview;
set_turn_config(&mut turn, config);
turn.set_permission_profile_for_tests(expected_permission_profile.clone());
turn.permission_profile = expected_permission_profile.clone();
assert_ne!(
expected_permission_profile,
turn.config.permissions.effective_permission_profile(),
@@ -4456,7 +4456,7 @@ async fn build_agent_spawn_config_uses_turn_context_values() {
&file_system_sandbox_policy,
network_sandbox_policy,
);
turn.set_permission_profile_for_tests(permission_profile.clone());
turn.permission_profile = permission_profile.clone();
turn.approval_policy
.set(AskForApproval::OnRequest)
.expect("approval policy set");
+1 -1
View File
@@ -172,7 +172,7 @@ async fn run_exec_like(args: RunExecLikeArgs) -> Result<FunctionToolOutput, Func
command: &exec_params.command,
approval_policy: turn.approval_policy.value(),
permission_profile: turn_environment.permission_profile().clone(),
windows_sandbox_level: turn_environment.settings.sandbox.windows_sandbox_level,
windows_sandbox_level: turn.windows_sandbox_level,
sandbox_permissions: if effective_additional_permissions.permissions_preapproved {
codex_protocol::models::SandboxPermissions::UseDefault
} else {
@@ -90,23 +90,21 @@ impl ShellCommandHandler {
turn_context: &TurnContext,
turn_environment: &TurnEnvironment,
cwd: AbsolutePathBuf,
allow_login_shell: bool,
) -> Result<ExecParams, FunctionCallError> {
let session_shell = session.user_shell();
let shell = turn_environment
.shell
.as_ref()
.unwrap_or(session_shell.as_ref());
let use_login_shell = Self::resolve_use_login_shell(
params.login,
turn_environment.settings.allow_login_shell,
)?;
let use_login_shell = Self::resolve_use_login_shell(params.login, allow_login_shell)?;
let command = Self::base_command(shell, &params.command, use_login_shell);
let mut env = create_env(
&turn_environment.settings.shell_environment_policy,
&turn_context.config.permissions.shell_environment_policy,
Some(session.thread_id),
);
let active_permission_profile = turn_environment.active_permission_profile();
let active_permission_profile = turn_context.config.permissions.active_permission_profile();
inject_permission_profile_env(&mut env, active_permission_profile.as_ref());
Ok(ExecParams {
@@ -121,10 +119,10 @@ impl ShellCommandHandler {
.flatten(),
network_environment_id: Some(turn_environment.environment_id.clone()),
sandbox_permissions: params.sandbox_permissions.unwrap_or_default(),
windows_sandbox_level: turn_environment.settings.sandbox.windows_sandbox_level,
windows_sandbox_private_desktop: turn_environment
.settings
.sandbox
windows_sandbox_level: turn_context.windows_sandbox_level,
windows_sandbox_private_desktop: turn_context
.config
.permissions
.windows_sandbox_private_desktop,
justification: params.justification.clone(),
arg0: None,
@@ -214,6 +212,7 @@ impl ShellCommandHandler {
turn.as_ref(),
&turn_environment,
cwd,
turn.config.permissions.allow_login_shell,
)?;
let shell_type = Some(
turn_environment
@@ -99,9 +99,7 @@ async fn shell_command_handler_to_exec_params_uses_selected_environment() {
let expected_command = selected_shell.derive_exec_args(&command, /*use_login_shell*/ true);
let selected_cwd = turn_context.config.cwd.join("selected-environment");
let expected_cwd = selected_cwd.join("subdir");
let mut selected_environment_settings = turn_context.primary_environment_settings().clone();
selected_environment_settings.allow_login_shell = true;
let selected_environment = TurnEnvironment::new_with_settings(
let selected_environment = TurnEnvironment::new(
"selected-environment".to_string(),
Arc::clone(
&turn_context
@@ -112,15 +110,13 @@ async fn shell_command_handler_to_exec_params_uses_selected_environment() {
),
PathUri::from_abs_path(&selected_cwd),
Vec::new(),
/*sandbox_override*/ None,
selected_environment_settings,
Some(selected_shell),
);
let mut expected_env = create_env(
&selected_environment.settings.shell_environment_policy,
&turn_context.config.permissions.shell_environment_policy,
Some(session.thread_id),
);
let active_permission_profile = selected_environment.active_permission_profile();
let active_permission_profile = turn_context.config.permissions.active_permission_profile();
inject_permission_profile_env(&mut expected_env, active_permission_profile.as_ref());
let params = ShellCommandToolCallParams {
@@ -140,6 +136,7 @@ async fn shell_command_handler_to_exec_params_uses_selected_environment() {
&turn_context,
&selected_environment,
expected_cwd.clone(),
/*allow_login_shell*/ true,
)
.expect("login shells should be allowed");
@@ -220,6 +217,7 @@ async fn shell_command_handler_defaults_to_non_login_when_disallowed() {
&turn_context,
turn_environment,
cwd,
/*allow_login_shell*/ false,
)
.expect("non-login shells should still be allowed");
@@ -158,8 +158,8 @@ impl ExecCommandHandler {
&turn_environment.file_system_sandbox_policy(),
turn_environment.network_sandbox_policy(),
SandboxablePreference::Auto,
turn_environment.settings.sandbox.windows_sandbox_level,
turn.network.is_some(),
turn.windows_sandbox_level,
turn.network.is_some() && turn_environment.uses_managed_network_proxy(),
);
// `to_abs_path()` alone cannot identify foreign drive paths: `file:///C:/repo` is
// representable as `/C:/repo` on POSIX. Require the inferred convention to match too.
@@ -233,7 +233,7 @@ impl ExecCommandHandler {
&args,
shell,
&shell_mode,
turn_environment.settings.allow_login_shell,
turn.config.permissions.allow_login_shell,
)
.map_err(FunctionCallError::RespondToModel)?;
let command = resolved_command.command;
@@ -147,8 +147,8 @@ impl ViewImageHandler {
))
})?;
let model_visible_path = path_uri.inferred_native_path_string();
let sandbox =
turn_environment.file_system_sandbox_context(/*additional_permissions*/ None);
let sandbox = turn
.file_system_sandbox_context(/*additional_permissions*/ None, turn_environment);
let fs = turn_environment.environment.get_filesystem();
let metadata = fs
@@ -315,7 +315,7 @@ mod tests {
replace_primary_environment_cwd(&mut turn, image_cwd.clone());
let image_path = image_cwd.join("image.png");
std::fs::write(image_path.as_path(), b"not a real image").expect("write test image");
turn.set_permission_profile_for_tests(PermissionProfile::read_only());
turn.permission_profile = PermissionProfile::read_only();
let turn = Arc::new(turn);
let result = ViewImageHandler::default()
+12 -9
View File
@@ -153,7 +153,6 @@ impl ToolOrchestrator {
let turn_environment = tool.turn_environment(req).clone();
let workspace_roots = turn_environment.workspace_roots();
let settings = &turn_environment.settings;
let permission_profile = turn_environment.permission_profile();
let materialized_workspace_roots = workspace_roots
.iter()
@@ -249,7 +248,7 @@ impl ToolOrchestrator {
&file_system_sandbox_policy,
network_sandbox_policy,
sandbox_preference,
settings.sandbox.windows_sandbox_level,
turn_ctx.windows_sandbox_level,
managed_network_active,
)
} else {
@@ -257,7 +256,7 @@ impl ToolOrchestrator {
};
// Platform-specific flag gating is handled by SandboxManager::select_initial.
let use_legacy_landlock = settings.sandbox.use_legacy_landlock;
let use_legacy_landlock = turn_ctx.config.features.use_legacy_landlock();
#[allow(deprecated)]
let sandbox_policy_cwd = tool
.sandbox_cwd(req)
@@ -274,8 +273,11 @@ impl ToolOrchestrator {
workspace_roots,
codex_linux_sandbox_exe: turn_ctx.config.codex_linux_sandbox_exe.as_ref(),
use_legacy_landlock,
windows_sandbox_level: settings.sandbox.windows_sandbox_level,
windows_sandbox_private_desktop: settings.sandbox.windows_sandbox_private_desktop,
windows_sandbox_level: turn_ctx.windows_sandbox_level,
windows_sandbox_private_desktop: turn_ctx
.config
.permissions
.windows_sandbox_private_desktop,
network_denial_cancellation_token: None,
network_proxy: None,
};
@@ -431,7 +433,7 @@ impl ToolOrchestrator {
&file_system_sandbox_policy,
network_sandbox_policy,
sandbox_preference,
settings.sandbox.windows_sandbox_level,
turn_ctx.windows_sandbox_level,
managed_network_active,
)
} else {
@@ -453,9 +455,10 @@ impl ToolOrchestrator {
workspace_roots,
codex_linux_sandbox_exe: retry_codex_linux_sandbox_exe,
use_legacy_landlock,
windows_sandbox_level: settings.sandbox.windows_sandbox_level,
windows_sandbox_private_desktop: settings
.sandbox
windows_sandbox_level: turn_ctx.windows_sandbox_level,
windows_sandbox_private_desktop: turn_ctx
.config
.permissions
.windows_sandbox_private_desktop,
network_denial_cancellation_token: None,
network_proxy: None,
+3 -3
View File
@@ -409,15 +409,15 @@ impl ToolRegistry {
(
"sandbox",
permission_profile_sandbox_tag(
&invocation.turn.permission_profile(),
invocation.turn.windows_sandbox_level(),
&invocation.turn.permission_profile,
invocation.turn.windows_sandbox_level,
invocation.turn.network.is_some(),
),
),
(
"sandbox_policy",
permission_profile_policy_tag(
&invocation.turn.permission_profile(),
&invocation.turn.permission_profile,
#[allow(deprecated)]
invocation.turn.cwd.as_path(),
),
@@ -202,7 +202,7 @@ pub(super) async fn try_run_zsh_fork(
sandbox_policy_cwd,
windows_sandbox_workspace_roots,
codex_linux_sandbox_exe: ctx.turn.config.codex_linux_sandbox_exe.clone(),
use_legacy_landlock: req.turn_environment.settings.sandbox.use_legacy_landlock,
use_legacy_landlock: ctx.turn.config.features.use_legacy_landlock(),
};
let main_execve_wrapper_exe = ctx
.session
@@ -241,7 +241,6 @@ pub(super) async fn try_run_zsh_fork(
environment_id: req.turn_environment.environment_id.clone(),
tool_name: GuardianCommandSource::Shell,
approval_policy: ctx.turn.approval_policy.value(),
windows_sandbox_level: req.turn_environment.settings.sandbox.windows_sandbox_level,
permission_profile: command_executor.permission_profile.clone(),
file_system_sandbox_policy: command_executor.file_system_sandbox_policy.clone(),
sandbox_permissions: req.sandbox_permissions,
@@ -316,7 +315,7 @@ pub(crate) async fn prepare_unified_exec_zsh_fork(
sandbox_policy_cwd,
windows_sandbox_workspace_roots: exec_request.windows_sandbox_workspace_roots.clone(),
codex_linux_sandbox_exe: ctx.turn.config.codex_linux_sandbox_exe.clone(),
use_legacy_landlock: req.turn_environment.settings.sandbox.use_legacy_landlock,
use_legacy_landlock: ctx.turn.config.features.use_legacy_landlock(),
};
let escalation_policy = CoreShellActionProvider {
policy: Arc::clone(&exec_policy),
@@ -326,7 +325,6 @@ pub(crate) async fn prepare_unified_exec_zsh_fork(
environment_id: req.turn_environment.environment_id.clone(),
tool_name: GuardianCommandSource::UnifiedExec,
approval_policy: ctx.turn.approval_policy.value(),
windows_sandbox_level: req.turn_environment.settings.sandbox.windows_sandbox_level,
permission_profile: exec_request.permission_profile.clone(),
file_system_sandbox_policy: exec_request.file_system_sandbox_policy.clone(),
sandbox_permissions: req.sandbox_permissions,
@@ -362,7 +360,6 @@ struct CoreShellActionProvider {
environment_id: String,
tool_name: GuardianCommandSource,
approval_policy: AskForApproval,
windows_sandbox_level: WindowsSandboxLevel,
permission_profile: PermissionProfile,
file_system_sandbox_policy: FileSystemSandboxPolicy,
sandbox_permissions: SandboxPermissions,
@@ -655,7 +652,7 @@ impl CoreShellActionProvider {
InterceptedExecPolicyContext {
approval_policy: self.approval_policy,
permission_profile: self.permission_profile.clone(),
windows_sandbox_level: self.windows_sandbox_level,
windows_sandbox_level: self.turn.windows_sandbox_level,
sandbox_permissions: self.approval_sandbox_permissions,
enable_shell_wrapper_parsing:
ENABLE_INTERCEPTED_EXEC_POLICY_SHELL_WRAPPER_PARSING,
@@ -429,7 +429,6 @@ async fn preapproved_additional_permissions_escalate_intercepted_exec() -> anyho
environment_id: "local".to_string(),
tool_name: GuardianCommandSource::Shell,
approval_policy: AskForApproval::OnRequest,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
permission_profile: permission_profile.clone(),
file_system_sandbox_policy: read_only_file_system_sandbox_policy(),
sandbox_permissions: SandboxPermissions::WithAdditionalPermissions,
@@ -548,10 +547,10 @@ async fn execve_permission_request_hook_short_circuits_prompt() -> anyhow::Resul
})));
turn_context.approval_policy = Constrained::allow_any(AskForApproval::OnRequest);
turn_context.set_permission_profile_for_tests(PermissionProfile::from_runtime_permissions(
turn_context.permission_profile = PermissionProfile::from_runtime_permissions(
&read_only_file_system_sandbox_policy(),
NetworkSandboxPolicy::Restricted,
));
);
let workdir = AbsolutePathBuf::try_from(std::env::current_dir()?)?;
let target = std::env::temp_dir().join("execve-hook-short-circuit.txt");
let target_str = target.display().to_string();
@@ -566,7 +565,6 @@ async fn execve_permission_request_hook_short_circuits_prompt() -> anyhow::Resul
environment_id: "local".to_string(),
tool_name: GuardianCommandSource::Shell,
approval_policy: AskForApproval::OnRequest,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
permission_profile: PermissionProfile::read_only(),
file_system_sandbox_policy: read_only_file_system_sandbox_policy(),
sandbox_permissions: SandboxPermissions::RequireEscalated,
@@ -778,7 +776,6 @@ prefix_rule(pattern = ["{cat_path_literal}"], decision = "allow")
environment_id: "local".to_string(),
tool_name: GuardianCommandSource::Shell,
approval_policy: AskForApproval::OnRequest,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
permission_profile,
file_system_sandbox_policy,
sandbox_permissions: SandboxPermissions::UseDefault,
@@ -822,7 +819,6 @@ async fn denied_reads_keep_granular_sandbox_rejection_for_escalation() -> anyhow
request_permissions: true,
mcp_elicitations: true,
}),
windows_sandbox_level: WindowsSandboxLevel::Disabled,
permission_profile,
file_system_sandbox_policy,
sandbox_permissions: SandboxPermissions::RequireEscalated,
+2 -9
View File
@@ -588,7 +588,7 @@ fn add_tool_sources(context: &CoreToolPlanContext<'_>, planned_tools: &mut Plann
if environment_mode.has_environment() {
let include_environment_id = matches!(environment_mode, ToolEnvironmentMode::Multiple);
planned_tools.add(ExecCommandHandler::new(ExecCommandHandlerOptions {
allow_login_shell: environments_allow_login_shell(context.step_context),
allow_login_shell: turn_context.config.permissions.allow_login_shell,
exec_permission_approvals_enabled: false,
include_environment_id,
include_shell_parameter: unified_exec_should_include_shell_parameter(
@@ -635,13 +635,6 @@ fn tool_environment_mode(step_context: &StepContext) -> ToolEnvironmentMode {
ToolEnvironmentMode::from_count(step_context.environments.turn_environments().count())
}
fn environments_allow_login_shell(step_context: &StepContext) -> bool {
step_context
.environments
.turn_environments()
.any(|environment| environment.settings.allow_login_shell)
}
#[instrument(level = "trace", skip_all)]
fn add_shell_tools(context: &CoreToolPlanContext<'_>, planned_tools: &mut PlannedTools) {
let turn_context = context.step_context.turn.as_ref();
@@ -651,7 +644,7 @@ fn add_shell_tools(context: &CoreToolPlanContext<'_>, planned_tools: &mut Planne
return;
}
let allow_login_shell = environments_allow_login_shell(context.step_context);
let allow_login_shell = turn_context.config.permissions.allow_login_shell;
let exec_permission_approvals_enabled = features.enabled(Feature::ExecPermissionApprovals);
let include_environment_id = matches!(environment_mode, ToolEnvironmentMode::Multiple);
let shell_command_options = ShellCommandHandlerOptions {
+1 -1
View File
@@ -84,7 +84,7 @@ fn test_exec_request(
ExecCapturePolicy::ShellTool,
SandboxType::None,
turn.config.effective_workspace_roots(),
turn.windows_sandbox_level(),
turn.windows_sandbox_level,
windows_sandbox_private_desktop,
permission_profile,
arg0,
@@ -1152,9 +1152,8 @@ impl UnifiedExecProcessManager {
cwd: PathUri,
context: &UnifiedExecContext,
) -> Result<(UnifiedExecProcess, Option<DeferredNetworkApproval>), UnifiedExecError> {
let turn_environment = &request.turn_environment;
let local_policy_env = create_env(
&turn_environment.settings.shell_environment_policy,
&context.turn.config.permissions.shell_environment_policy,
/*thread_id*/ None,
);
let mut env = local_policy_env.clone();
@@ -1162,12 +1161,12 @@ impl UnifiedExecProcessManager {
CODEX_THREAD_ID_ENV_VAR.to_string(),
context.session.thread_id.to_string(),
);
let active_permission_profile = turn_environment.active_permission_profile();
let active_permission_profile = context.turn.config.permissions.active_permission_profile();
inject_permission_profile_env(&mut env, active_permission_profile.as_ref());
let env = apply_unified_exec_env(env);
let exec_server_env_config = ExecServerEnvConfig {
policy: exec_env_policy_from_shell_policy(
&turn_environment.settings.shell_environment_policy,
&context.turn.config.permissions.shell_environment_policy,
),
local_policy_env,
};
@@ -1180,8 +1179,8 @@ impl UnifiedExecProcessManager {
.create_exec_approval_requirement_for_command(ExecApprovalRequest {
command: &request.command,
approval_policy: context.turn.approval_policy.value(),
permission_profile: turn_environment.permission_profile().clone(),
windows_sandbox_level: turn_environment.settings.sandbox.windows_sandbox_level,
permission_profile: request.turn_environment.permission_profile().clone(),
windows_sandbox_level: context.turn.windows_sandbox_level,
sandbox_permissions: if request.additional_permissions_preapproved {
crate::sandboxing::SandboxPermissions::UseDefault
} else {
@@ -1200,8 +1199,10 @@ impl UnifiedExecProcessManager {
turn_environment: request.turn_environment.clone(),
env,
exec_server_env_config: Some(exec_server_env_config),
explicit_env_overrides: turn_environment
.settings
explicit_env_overrides: context
.turn
.config
.permissions
.shell_environment_policy
.r#set
.clone(),
+3 -3
View File
@@ -111,7 +111,7 @@ pub fn local(cwd: AbsolutePathBuf) -> TurnEnvironmentSelection {
environment_id: codex_exec_server::LOCAL_ENVIRONMENT_ID.to_string(),
cwd: PathUri::from_abs_path(&cwd),
workspace_roots: vec![PathUri::from_abs_path(&cwd)],
sandbox: None,
permission_profile: None,
}
}
@@ -144,7 +144,7 @@ impl TestEnv {
environment_id: codex_exec_server::REMOTE_ENVIRONMENT_ID.to_string(),
cwd: PathUri::from_abs_path(&cwd),
workspace_roots: vec![PathUri::from_abs_path(&cwd)],
sandbox: None,
permission_profile: None,
},
None => local(cwd.clone()),
};
@@ -213,7 +213,7 @@ pub async fn test_env() -> Result<TestEnv> {
environment_id: codex_exec_server::REMOTE_ENVIRONMENT_ID.to_string(),
cwd: cwd_uri.clone(),
workspace_roots: vec![cwd_uri.clone()],
sandbox: None,
permission_profile: None,
};
let cwd = if remote_env == TestEnvironment::WineExec {
// TODO(anp): Convert `Config::cwd` to `LegacyAppPathString` and remove this
@@ -111,6 +111,7 @@ async fn windows_exec_server_runs_with_native_shell_and_cwd() -> Result<()> {
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
cwd: cwd.clone(),
workspace_roots: vec![cwd],
permission_profile: None,
}
}],
);
+2 -2
View File
@@ -706,13 +706,13 @@ async fn multi_environment_thread_loads_every_project_and_keeps_creation_snapsho
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
cwd: PathUri::from_abs_path(&test.config.cwd),
workspace_roots: vec![PathUri::from_abs_path(&test.config.cwd)],
sandbox: None,
permission_profile: None,
},
TurnEnvironmentSelection {
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
cwd: PathUri::from_host_native_path(local_root.path())?,
workspace_roots: vec![PathUri::from_host_native_path(local_root.path())?],
sandbox: None,
permission_profile: None,
},
],
thread_extension_init: Default::default(),
+1 -1
View File
@@ -1654,7 +1654,7 @@ async fn apply_patch_turn_diff_tracks_local_and_remote_environment_paths() -> Re
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
cwd: PathUri::from_abs_path(&shared_cwd),
workspace_roots: vec![PathUri::from_abs_path(&shared_cwd)],
sandbox: None,
permission_profile: None,
},
];
test.codex
@@ -115,7 +115,7 @@ async fn model_visible_environment_context_preserves_foreign_workspace_roots() -
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
cwd: PathUri::from_abs_path(&test.config.cwd),
workspace_roots: vec![foreign_root],
sandbox: None,
permission_profile: None,
}],
)),
..Default::default()
@@ -11,7 +11,6 @@ use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::Op;
use codex_protocol::protocol::ThreadSettingsOverrides;
use codex_protocol::protocol::TurnEnvironmentSandbox;
use codex_protocol::protocol::TurnEnvironmentSelection;
use codex_protocol::protocol::TurnEnvironmentSelections;
use codex_protocol::user_input::UserInput;
@@ -186,37 +185,28 @@ async fn two_exec_servers_isolate_workspace_write_roots() -> Result<()> {
let first_workspace_uri = PathUri::from_host_native_path(first_workspace.path())?;
let second_workspace_uri = PathUri::from_host_native_path(second_workspace.path())?;
let environment_sandbox = TurnEnvironmentSandbox {
permission_profile: PermissionProfile::workspace_write_with(
&[],
NetworkSandboxPolicy::Restricted,
/*exclude_tmpdir_env_var*/ true,
/*exclude_slash_tmp*/ true,
),
active_permission_profile: None,
windows_sandbox_level: Default::default(),
windows_sandbox_private_desktop: false,
use_legacy_landlock: false,
};
let environment_permission_profile = PermissionProfile::workspace_write_with(
&[],
NetworkSandboxPolicy::Restricted,
/*exclude_tmpdir_env_var*/ true,
/*exclude_slash_tmp*/ true,
);
let environments = vec![
TurnEnvironmentSelection {
environment_id: FIRST_ENVIRONMENT_ID.to_string(),
cwd: first_workspace_uri.clone(),
workspace_roots: vec![first_workspace_uri],
sandbox: Some(environment_sandbox.clone()),
permission_profile: Some(environment_permission_profile.clone()),
},
TurnEnvironmentSelection {
environment_id: SECOND_ENVIRONMENT_ID.to_string(),
cwd: second_workspace_uri.clone(),
workspace_roots: vec![second_workspace_uri],
sandbox: Some(environment_sandbox),
permission_profile: Some(environment_permission_profile),
},
];
// Keep the thread default unrestricted. This makes the cross-write
// assertions prove that execution used each environment's sandbox.
let permission_profile = PermissionProfile::Disabled;
let (sandbox_policy, permission_profile) =
turn_permission_fields(permission_profile, test.config.cwd.as_path());
turn_permission_fields(PermissionProfile::Disabled, test.config.cwd.as_path());
test.codex
.submit(Op::UserInput {
items: vec![UserInput::Text {
@@ -1068,7 +1068,7 @@ async fn approved_network_host_for_one_environment_still_prompts_in_another() ->
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
cwd: PathUri::from_abs_path(&remote_cwd),
workspace_roots: vec![PathUri::from_abs_path(&remote_cwd)],
sandbox: None,
permission_profile: None,
},
];
+10 -10
View File
@@ -319,7 +319,7 @@ async fn explicit_remote_shell_runs_in_remote_cwd() -> Result<()> {
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
cwd: PathUri::from_abs_path(&test.config.cwd),
workspace_roots: vec![PathUri::from_abs_path(&test.config.cwd)],
sandbox: None,
permission_profile: None,
}]),
)
.await?;
@@ -709,7 +709,7 @@ async fn deferred_executor_starts_noise_connection_after_registration() -> Resul
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
cwd: PathUri::from_abs_path(&test.config.cwd),
workspace_roots: vec![PathUri::from_abs_path(&test.config.cwd)],
sandbox: None,
permission_profile: None,
}],
)),
..Default::default()
@@ -904,7 +904,7 @@ async fn deferred_executor_wait_reports_startup_failure() -> Result<()> {
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
cwd: PathUri::from_abs_path(&test.config.cwd),
workspace_roots: vec![PathUri::from_abs_path(&test.config.cwd)],
sandbox: None,
permission_profile: None,
}],
)),
..Default::default()
@@ -1254,7 +1254,7 @@ async fn exec_command_routes_to_selected_remote_environment() -> Result<()> {
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
cwd: PathUri::from_abs_path(&remote_cwd),
workspace_roots: vec![PathUri::from_abs_path(&remote_cwd)],
sandbox: None,
permission_profile: None,
};
let multi_env_output = exec_command_routing_output(
&test,
@@ -1394,13 +1394,13 @@ async fn remote_exec_materializes_target_roots_before_sandbox_selection() -> Res
environment_id: LOCAL_ENVIRONMENT_ID.to_string(),
cwd: PathUri::from_abs_path(&local_cwd.path().abs()),
workspace_roots: Vec::new(),
sandbox: None,
permission_profile: None,
},
TurnEnvironmentSelection {
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
cwd: remote_cwd_uri.clone(),
workspace_roots: vec![remote_cwd_uri.clone()],
sandbox: None,
permission_profile: None,
},
],
)),
@@ -1569,7 +1569,7 @@ async fn remote_request_permissions_grant_unblocks_later_remote_exec() -> Result
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
cwd: PathUri::from_abs_path(&remote_cwd),
workspace_roots: vec![PathUri::from_abs_path(&remote_cwd)],
sandbox: None,
permission_profile: None,
},
],
AskForApproval::OnRequest,
@@ -1712,7 +1712,7 @@ async fn apply_patch_freeform_routes_to_selected_remote_environment() -> Result<
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
cwd: PathUri::from_abs_path(&remote_cwd),
workspace_roots: vec![PathUri::from_abs_path(&remote_cwd)],
sandbox: None,
permission_profile: None,
},
]),
)
@@ -1797,7 +1797,7 @@ async fn apply_patch_approvals_are_remembered_per_environment() -> Result<()> {
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
cwd: PathUri::from_abs_path(&remote_cwd),
workspace_roots: vec![PathUri::from_abs_path(&remote_cwd)],
sandbox: None,
permission_profile: None,
},
];
let local_patch = format!(
@@ -2000,7 +2000,7 @@ async fn apply_patch_intercepted_exec_command_routes_to_selected_remote_environm
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
cwd: PathUri::from_abs_path(&remote_cwd),
workspace_roots: vec![PathUri::from_abs_path(&remote_cwd)],
sandbox: None,
permission_profile: None,
},
]),
)
@@ -39,7 +39,6 @@ fn resume_history(
permission_profile: None,
network: None,
file_system_sandbox_policy: None,
environment_sandboxes: None,
model: previous_model.to_string(),
comp_hash: None,
personality: None,
+1 -1
View File
@@ -587,7 +587,7 @@ async fn view_image_routes_to_selected_remote_environment() -> anyhow::Result<()
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
cwd: remote_cwd_uri.clone(),
workspace_roots: vec![remote_cwd_uri],
sandbox: None,
permission_profile: None,
};
let relative_call_id = "call-view-image-relative-multi-env";
let absolute_call_id = "call-view-image-absolute-multi-env";
@@ -201,6 +201,7 @@ async fn selected_executor_catalog_follows_step_availability_and_reuses_its_cach
environment_id: "turn-env".to_string(),
cwd: PathUri::parse("file:///workspace").expect("cwd URI"),
workspace_roots: Vec::new(),
permission_profile: None,
};
let available_sections = registry.context_contributors()[0]
.contribute_world_state(WorldStateContributionInput {
@@ -601,6 +602,7 @@ async fn root_qualified_locator_selects_only_the_matching_executor_skill() -> Te
environment_id: "env-1".to_string(),
cwd: PathUri::parse("file:///workspace").expect("cwd URI"),
workspace_roots: Vec::new(),
permission_profile: None,
}],
ready_selected_capability_roots: &selected_roots,
executor_capability_discovery: None,
+3 -34
View File
@@ -138,36 +138,9 @@ pub struct TurnEnvironmentSelection {
pub environment_id: String,
pub cwd: PathUri,
pub workspace_roots: Vec<PathUri>,
pub sandbox: Option<TurnEnvironmentSandbox>,
}
/// Sandbox settings captured for one selected environment.
///
/// These settings travel with [`TurnEnvironmentSelection`] so environment
/// forwarding cannot silently drop its sandbox.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, TS)]
pub struct TurnEnvironmentSandbox {
pub permission_profile: PermissionProfile,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub active_permission_profile: Option<ActivePermissionProfile>,
pub windows_sandbox_level: WindowsSandboxLevel,
#[serde(default)]
pub windows_sandbox_private_desktop: bool,
#[serde(default)]
pub use_legacy_landlock: bool,
}
impl Default for TurnEnvironmentSandbox {
fn default() -> Self {
Self {
permission_profile: PermissionProfile::default(),
active_permission_profile: None,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
windows_sandbox_private_desktop: false,
use_legacy_landlock: false,
}
}
/// Explicit permissions for this environment. `None` inherits the thread
/// permission profile.
pub permission_profile: Option<PermissionProfile>,
}
#[derive(Debug, Clone, PartialEq)]
@@ -3337,10 +3310,6 @@ pub struct TurnContextItem {
pub network: Option<TurnContextNetworkItem>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub file_system_sandbox_policy: Option<FileSystemSandboxPolicy>,
/// Explicit sandbox overrides captured for selected environments. Older
/// rollout items omit this and inherit the top-level permission fields.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub environment_sandboxes: Option<BTreeMap<String, TurnEnvironmentSandbox>>,
pub model: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub comp_hash: Option<String>,
-1
View File
@@ -1510,7 +1510,6 @@ async fn resume_candidate_matches_cwd_reads_latest_turn_context() -> std::io::Re
permission_profile: None,
network: None,
file_system_sandbox_policy: None,
environment_sandboxes: None,
model: "test-model".to_string(),
comp_hash: None,
personality: None,
-4
View File
@@ -430,7 +430,6 @@ mod tests {
permission_profile: None,
network: None,
file_system_sandbox_policy: None,
environment_sandboxes: None,
model: "gpt-5".to_string(),
comp_hash: None,
personality: None,
@@ -477,7 +476,6 @@ mod tests {
permission_profile: Some(permission_profile.clone()),
network: None,
file_system_sandbox_policy: None,
environment_sandboxes: None,
model: "gpt-5".to_string(),
comp_hash: None,
personality: None,
@@ -520,7 +518,6 @@ mod tests {
permission_profile: None,
network: None,
file_system_sandbox_policy: None,
environment_sandboxes: None,
model: "gpt-5".to_string(),
comp_hash: None,
personality: None,
@@ -560,7 +557,6 @@ mod tests {
permission_profile: None,
network: None,
file_system_sandbox_policy: None,
environment_sandboxes: None,
model: "gpt-5".to_string(),
comp_hash: None,
personality: None,
-1
View File
@@ -584,7 +584,6 @@ mod tests {
permission_profile: None,
network: None,
file_system_sandbox_policy: None,
environment_sandboxes: None,
model: model.to_string(),
comp_hash: None,
personality: None,
@@ -374,7 +374,6 @@ fn turn_context(root: &Path, turn_id: &str) -> RolloutItem {
permission_profile: None,
network: None,
file_system_sandbox_policy: None,
environment_sandboxes: None,
model: "test-model".to_string(),
comp_hash: None,
personality: None,