Merge branch 'main' of https://github.com/openai/codex into dev/mzeng/new_elicitation

This commit is contained in:
Matthew Zeng
2026-03-05 23:10:24 -08:00
137 changed files with 8413 additions and 2446 deletions
+174 -50
View File
@@ -67,23 +67,6 @@ impl FileSystemPermissions {
}
}
#[derive(Debug, Clone, Default, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema, TS)]
pub struct MacOsPermissions {
pub preferences: Option<MacOsPreferencesValue>,
pub automations: Option<MacOsAutomationValue>,
pub accessibility: Option<bool>,
pub calendar: Option<bool>,
}
impl MacOsPermissions {
pub fn is_empty(&self) -> bool {
self.preferences.is_none()
&& self.automations.is_none()
&& self.accessibility.is_none()
&& self.calendar.is_none()
}
}
#[derive(Debug, Clone, Default, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema, TS)]
pub struct NetworkPermissions {
pub enabled: Option<bool>,
@@ -95,31 +78,32 @@ impl NetworkPermissions {
}
}
#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema, TS)]
#[serde(untagged)]
pub enum MacOsPreferencesValue {
Bool(bool),
Mode(String),
}
#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema, TS)]
#[serde(untagged)]
pub enum MacOsAutomationValue {
Bool(bool),
BundleIds(Vec<String>),
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[derive(
Debug,
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
Default,
Hash,
Serialize,
Deserialize,
JsonSchema,
TS,
)]
#[serde(rename_all = "snake_case")]
pub enum MacOsPreferencesPermission {
None,
// IMPORTANT: ReadOnly needs to be the default because it's the
// security-sensitive default and keeps cf prefs working.
#[default]
ReadOnly,
ReadWrite,
None,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[derive(Debug, Clone, PartialEq, Eq, Default, Hash, Serialize, Deserialize, JsonSchema, TS)]
#[serde(rename_all = "snake_case", try_from = "MacOsAutomationPermissionDe")]
pub enum MacOsAutomationPermission {
#[default]
None,
@@ -127,7 +111,56 @@ pub enum MacOsAutomationPermission {
BundleIds(Vec<String>),
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum MacOsAutomationPermissionDe {
Mode(String),
BundleIds(Vec<String>),
BundleIdsObject { bundle_ids: Vec<String> },
}
impl TryFrom<MacOsAutomationPermissionDe> for MacOsAutomationPermission {
type Error = String;
/// Accepts one of:
/// - `"none"` or `"all"`
/// - a plain list of bundle IDs, e.g. `["com.apple.Notes"]`
/// - an object with bundle IDs, e.g. `{"bundle_ids": ["com.apple.Notes"]}`
fn try_from(value: MacOsAutomationPermissionDe) -> Result<Self, Self::Error> {
let permission = match value {
MacOsAutomationPermissionDe::Mode(value) => {
let normalized = value.trim().to_ascii_lowercase();
if normalized == "all" {
MacOsAutomationPermission::All
} else if normalized == "none" {
MacOsAutomationPermission::None
} else {
return Err(format!(
"invalid macOS automation permission: {value}; expected none, all, or bundle ids"
));
}
}
MacOsAutomationPermissionDe::BundleIds(bundle_ids)
| MacOsAutomationPermissionDe::BundleIdsObject { bundle_ids } => {
let bundle_ids = bundle_ids
.into_iter()
.map(|bundle_id| bundle_id.trim().to_string())
.filter(|bundle_id| !bundle_id.is_empty())
.collect::<Vec<String>>();
if bundle_ids.is_empty() {
MacOsAutomationPermission::None
} else {
MacOsAutomationPermission::BundleIds(bundle_ids)
}
}
};
Ok(permission)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Hash, Serialize, Deserialize, JsonSchema, TS)]
#[serde(default)]
pub struct MacOsSeatbeltProfileExtensions {
pub macos_preferences: MacOsPreferencesPermission,
pub macos_automation: MacOsAutomationPermission,
@@ -139,25 +172,12 @@ pub struct MacOsSeatbeltProfileExtensions {
pub struct PermissionProfile {
pub network: Option<NetworkPermissions>,
pub file_system: Option<FileSystemPermissions>,
pub macos: Option<MacOsPermissions>,
pub macos: Option<MacOsSeatbeltProfileExtensions>,
}
impl PermissionProfile {
pub fn is_empty(&self) -> bool {
self.network
.as_ref()
.map(NetworkPermissions::is_empty)
.unwrap_or(true)
&& self
.file_system
.as_ref()
.map(FileSystemPermissions::is_empty)
.unwrap_or(true)
&& self
.macos
.as_ref()
.map(MacOsPermissions::is_empty)
.unwrap_or(true)
self.network.is_none() && self.file_system.is_none() && self.macos.is_none()
}
}
@@ -1346,6 +1366,110 @@ mod tests {
);
}
#[test]
fn permission_profile_is_empty_when_all_fields_are_none() {
assert_eq!(PermissionProfile::default().is_empty(), true);
}
#[test]
fn permission_profile_is_not_empty_when_field_is_present_but_nested_empty() {
let permission_profile = PermissionProfile {
network: Some(NetworkPermissions { enabled: None }),
file_system: None,
macos: None,
};
assert_eq!(permission_profile.is_empty(), false);
}
#[test]
fn macos_preferences_permission_deserializes_read_write() {
let permission = serde_json::from_str::<MacOsPreferencesPermission>("\"read_write\"")
.expect("deserialize macos preferences permission");
assert_eq!(permission, MacOsPreferencesPermission::ReadWrite);
}
#[test]
fn macos_preferences_permission_order_matches_permissiveness() {
assert!(MacOsPreferencesPermission::None < MacOsPreferencesPermission::ReadOnly);
assert!(MacOsPreferencesPermission::ReadOnly < MacOsPreferencesPermission::ReadWrite);
}
#[test]
fn permission_profile_deserializes_macos_seatbelt_profile_extensions() {
let permission_profile = serde_json::from_value::<PermissionProfile>(serde_json::json!({
"network": null,
"file_system": null,
"macos": {
"macos_preferences": "read_write",
"macos_automation": ["com.apple.Notes"],
"macos_accessibility": true,
"macos_calendar": true
}
}))
.expect("deserialize permission profile");
assert_eq!(
permission_profile,
PermissionProfile {
network: None,
file_system: None,
macos: Some(MacOsSeatbeltProfileExtensions {
macos_preferences: MacOsPreferencesPermission::ReadWrite,
macos_automation: MacOsAutomationPermission::BundleIds(vec![
"com.apple.Notes".to_string(),
]),
macos_accessibility: true,
macos_calendar: true,
}),
}
);
}
#[test]
fn macos_seatbelt_profile_extensions_deserializes_missing_fields_to_defaults() {
let permissions =
serde_json::from_value::<MacOsSeatbeltProfileExtensions>(serde_json::json!({
"macos_automation": ["com.apple.Notes"]
}))
.expect("deserialize macos permissions");
assert_eq!(
permissions,
MacOsSeatbeltProfileExtensions {
macos_preferences: MacOsPreferencesPermission::ReadOnly,
macos_automation: MacOsAutomationPermission::BundleIds(vec![
"com.apple.Notes".to_string(),
]),
macos_accessibility: false,
macos_calendar: false,
}
);
}
#[test]
fn macos_automation_permission_deserializes_all_and_none() {
let all = serde_json::from_str::<MacOsAutomationPermission>("\"all\"")
.expect("deserialize all automation permission");
let none = serde_json::from_str::<MacOsAutomationPermission>("\"none\"")
.expect("deserialize none automation permission");
assert_eq!(all, MacOsAutomationPermission::All);
assert_eq!(none, MacOsAutomationPermission::None);
}
#[test]
fn macos_automation_permission_deserializes_bundle_ids_object() {
let permission = serde_json::from_value::<MacOsAutomationPermission>(serde_json::json!({
"bundle_ids": ["com.apple.Notes"]
}))
.expect("deserialize bundle_ids object automation permission");
assert_eq!(
permission,
MacOsAutomationPermission::BundleIds(vec!["com.apple.Notes".to_string(),])
);
}
#[test]
fn convert_mcp_content_to_items_builds_data_urls_when_missing_prefix() {
let contents = vec![serde_json::json!({
@@ -4,12 +4,17 @@ Commands may require user approval before execution. Prefer requesting sandboxed
## Preferred request mode
When you need extra filesystem access for one command, use:
When you need extra sandboxed permissions for one command, use:
- `sandbox_permissions: "with_additional_permissions"`
- `additional_permissions` with one or both fields:
- `additional_permissions` with one or more of:
- `network.enabled`: set to `true` to enable network access
- `file_system.read`: list of paths that need read access
- `file_system.write`: list of paths that need write access
- `macos.preferences`: `readonly` or `readwrite`
- `macos.automations`: list of bundle IDs that need Apple Events access
- `macos.accessibility`: set to `true` to allow accessibility APIs
- `macos.calendar`: set to `true` to allow Calendar access
This keeps execution inside the current sandbox policy, while adding only the requested permissions for that command, unless an exec-policy allow rule applies and authorizes running the command outside the sandbox.
+4 -1
View File
@@ -2223,6 +2223,8 @@ pub struct TurnContextNetworkItem {
pub struct TurnContextItem {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub turn_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trace_id: Option<String>,
pub cwd: PathBuf,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub current_date: Option<String>,
@@ -2803,7 +2805,6 @@ pub struct SkillsListEntry {
pub struct SessionNetworkProxyRuntime {
pub http_addr: String,
pub socks_addr: String,
pub admin_addr: String,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, TS)]
@@ -3512,6 +3513,7 @@ mod tests {
"summary": "auto",
}))?;
assert_eq!(item.trace_id, None);
assert_eq!(item.network, None);
Ok(())
}
@@ -3520,6 +3522,7 @@ mod tests {
fn turn_context_item_serializes_network_when_present() -> Result<()> {
let item = TurnContextItem {
turn_id: None,
trace_id: None,
cwd: PathBuf::from("/tmp"),
current_date: None,
timezone: None,