diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json index 71ea5952a1..9014611604 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json @@ -7569,6 +7569,36 @@ ], "type": "string" }, + "BrowserUseConfig": { + "properties": { + "allow_history_access": { + "type": [ + "boolean", + "null" + ] + }, + "default_origin_policy": { + "anyOf": [ + { + "$ref": "#/definitions/v2/BrowserUseOriginPolicyConfig" + }, + { + "type": "null" + } + ] + }, + "origins": { + "additionalProperties": { + "$ref": "#/definitions/v2/BrowserUseOriginPolicyConfig" + }, + "type": [ + "object", + "null" + ] + } + }, + "type": "object" + }, "BrowserUseOriginPolicy": { "properties": { "access": { @@ -7640,6 +7670,51 @@ }, "type": "object" }, + "BrowserUseOriginPolicyConfig": { + "properties": { + "access": { + "anyOf": [ + { + "$ref": "#/definitions/v2/AllowDenyRequirement" + }, + { + "type": "null" + } + ] + }, + "downloads": { + "anyOf": [ + { + "$ref": "#/definitions/v2/AllowDenyRequirement" + }, + { + "type": "null" + } + ] + }, + "full_cdp_access": { + "anyOf": [ + { + "$ref": "#/definitions/v2/AllowDenyRequirement" + }, + { + "type": "null" + } + ] + }, + "uploads": { + "anyOf": [ + { + "$ref": "#/definitions/v2/AllowDenyRequirement" + }, + { + "type": "null" + } + ] + } + }, + "type": "object" + }, "BrowserUseRequirements": { "properties": { "allowGlobalPersistentApproval": { @@ -8481,6 +8556,55 @@ ], "type": "object" }, + "ComputerUseConfig": { + "properties": { + "default_app_access": { + "anyOf": [ + { + "$ref": "#/definitions/v2/AllowDenyRequirement" + }, + { + "type": "null" + } + ] + }, + "macos": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ComputerUseMacosConfig" + }, + { + "type": "null" + } + ] + }, + "windows": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ComputerUseWindowsConfig" + }, + { + "type": "null" + } + ] + } + }, + "type": "object" + }, + "ComputerUseMacosConfig": { + "properties": { + "bundle_ids": { + "additionalProperties": { + "$ref": "#/definitions/v2/AllowDenyRequirement" + }, + "type": [ + "object", + "null" + ] + } + }, + "type": "object" + }, "ComputerUseMacosRequirements": { "properties": { "bundleIds": { @@ -8542,6 +8666,54 @@ }, "type": "object" }, + "ComputerUseWindowsConfig": { + "properties": { + "aumids": { + "additionalProperties": { + "$ref": "#/definitions/v2/AllowDenyRequirement" + }, + "type": [ + "object", + "null" + ] + }, + "exes": { + "items": { + "$ref": "#/definitions/v2/ComputerUseWindowsExeConfig" + }, + "type": [ + "array", + "null" + ] + } + }, + "type": "object" + }, + "ComputerUseWindowsExeConfig": { + "properties": { + "access": { + "$ref": "#/definitions/v2/AllowDenyRequirement" + }, + "binary_name": { + "type": [ + "string", + "null" + ] + }, + "product_name": { + "type": "string" + }, + "publisher_name": { + "type": "string" + } + }, + "required": [ + "access", + "product_name", + "publisher_name" + ], + "type": "object" + }, "ComputerUseWindowsExeRequirement": { "properties": { "access": { @@ -8624,12 +8796,32 @@ ], "description": "[UNSTABLE] Optional default for where approval requests are routed for review." }, + "browser_use": { + "anyOf": [ + { + "$ref": "#/definitions/v2/BrowserUseConfig" + }, + { + "type": "null" + } + ] + }, "compact_prompt": { "type": [ "string", "null" ] }, + "computer_use": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ComputerUseConfig" + }, + { + "type": "null" + } + ] + }, "desktop": { "additionalProperties": true, "type": [ diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json index 08a4feba99..83059e0203 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json @@ -1294,6 +1294,36 @@ ], "type": "string" }, + "BrowserUseConfig": { + "properties": { + "allow_history_access": { + "type": [ + "boolean", + "null" + ] + }, + "default_origin_policy": { + "anyOf": [ + { + "$ref": "#/definitions/BrowserUseOriginPolicyConfig" + }, + { + "type": "null" + } + ] + }, + "origins": { + "additionalProperties": { + "$ref": "#/definitions/BrowserUseOriginPolicyConfig" + }, + "type": [ + "object", + "null" + ] + } + }, + "type": "object" + }, "BrowserUseOriginPolicy": { "properties": { "access": { @@ -1365,6 +1395,51 @@ }, "type": "object" }, + "BrowserUseOriginPolicyConfig": { + "properties": { + "access": { + "anyOf": [ + { + "$ref": "#/definitions/AllowDenyRequirement" + }, + { + "type": "null" + } + ] + }, + "downloads": { + "anyOf": [ + { + "$ref": "#/definitions/AllowDenyRequirement" + }, + { + "type": "null" + } + ] + }, + "full_cdp_access": { + "anyOf": [ + { + "$ref": "#/definitions/AllowDenyRequirement" + }, + { + "type": "null" + } + ] + }, + "uploads": { + "anyOf": [ + { + "$ref": "#/definitions/AllowDenyRequirement" + }, + { + "type": "null" + } + ] + } + }, + "type": "object" + }, "BrowserUseRequirements": { "properties": { "allowGlobalPersistentApproval": { @@ -4519,6 +4594,55 @@ ], "type": "object" }, + "ComputerUseConfig": { + "properties": { + "default_app_access": { + "anyOf": [ + { + "$ref": "#/definitions/AllowDenyRequirement" + }, + { + "type": "null" + } + ] + }, + "macos": { + "anyOf": [ + { + "$ref": "#/definitions/ComputerUseMacosConfig" + }, + { + "type": "null" + } + ] + }, + "windows": { + "anyOf": [ + { + "$ref": "#/definitions/ComputerUseWindowsConfig" + }, + { + "type": "null" + } + ] + } + }, + "type": "object" + }, + "ComputerUseMacosConfig": { + "properties": { + "bundle_ids": { + "additionalProperties": { + "$ref": "#/definitions/AllowDenyRequirement" + }, + "type": [ + "object", + "null" + ] + } + }, + "type": "object" + }, "ComputerUseMacosRequirements": { "properties": { "bundleIds": { @@ -4580,6 +4704,54 @@ }, "type": "object" }, + "ComputerUseWindowsConfig": { + "properties": { + "aumids": { + "additionalProperties": { + "$ref": "#/definitions/AllowDenyRequirement" + }, + "type": [ + "object", + "null" + ] + }, + "exes": { + "items": { + "$ref": "#/definitions/ComputerUseWindowsExeConfig" + }, + "type": [ + "array", + "null" + ] + } + }, + "type": "object" + }, + "ComputerUseWindowsExeConfig": { + "properties": { + "access": { + "$ref": "#/definitions/AllowDenyRequirement" + }, + "binary_name": { + "type": [ + "string", + "null" + ] + }, + "product_name": { + "type": "string" + }, + "publisher_name": { + "type": "string" + } + }, + "required": [ + "access", + "product_name", + "publisher_name" + ], + "type": "object" + }, "ComputerUseWindowsExeRequirement": { "properties": { "access": { @@ -4662,12 +4834,32 @@ ], "description": "[UNSTABLE] Optional default for where approval requests are routed for review." }, + "browser_use": { + "anyOf": [ + { + "$ref": "#/definitions/BrowserUseConfig" + }, + { + "type": "null" + } + ] + }, "compact_prompt": { "type": [ "string", "null" ] }, + "computer_use": { + "anyOf": [ + { + "$ref": "#/definitions/ComputerUseConfig" + }, + { + "type": "null" + } + ] + }, "desktop": { "additionalProperties": true, "type": [ diff --git a/codex-rs/app-server-protocol/schema/json/v2/ConfigReadResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ConfigReadResponse.json index 3d028211e7..c85ec90dde 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ConfigReadResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ConfigReadResponse.json @@ -5,6 +5,13 @@ "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" }, + "AllowDenyRequirement": { + "enum": [ + "allow", + "deny" + ], + "type": "string" + }, "AnalyticsConfig": { "additionalProperties": true, "properties": { @@ -237,6 +244,178 @@ } ] }, + "BrowserUseConfig": { + "properties": { + "allow_history_access": { + "type": [ + "boolean", + "null" + ] + }, + "default_origin_policy": { + "anyOf": [ + { + "$ref": "#/definitions/BrowserUseOriginPolicyConfig" + }, + { + "type": "null" + } + ] + }, + "origins": { + "additionalProperties": { + "$ref": "#/definitions/BrowserUseOriginPolicyConfig" + }, + "type": [ + "object", + "null" + ] + } + }, + "type": "object" + }, + "BrowserUseOriginPolicyConfig": { + "properties": { + "access": { + "anyOf": [ + { + "$ref": "#/definitions/AllowDenyRequirement" + }, + { + "type": "null" + } + ] + }, + "downloads": { + "anyOf": [ + { + "$ref": "#/definitions/AllowDenyRequirement" + }, + { + "type": "null" + } + ] + }, + "full_cdp_access": { + "anyOf": [ + { + "$ref": "#/definitions/AllowDenyRequirement" + }, + { + "type": "null" + } + ] + }, + "uploads": { + "anyOf": [ + { + "$ref": "#/definitions/AllowDenyRequirement" + }, + { + "type": "null" + } + ] + } + }, + "type": "object" + }, + "ComputerUseConfig": { + "properties": { + "default_app_access": { + "anyOf": [ + { + "$ref": "#/definitions/AllowDenyRequirement" + }, + { + "type": "null" + } + ] + }, + "macos": { + "anyOf": [ + { + "$ref": "#/definitions/ComputerUseMacosConfig" + }, + { + "type": "null" + } + ] + }, + "windows": { + "anyOf": [ + { + "$ref": "#/definitions/ComputerUseWindowsConfig" + }, + { + "type": "null" + } + ] + } + }, + "type": "object" + }, + "ComputerUseMacosConfig": { + "properties": { + "bundle_ids": { + "additionalProperties": { + "$ref": "#/definitions/AllowDenyRequirement" + }, + "type": [ + "object", + "null" + ] + } + }, + "type": "object" + }, + "ComputerUseWindowsConfig": { + "properties": { + "aumids": { + "additionalProperties": { + "$ref": "#/definitions/AllowDenyRequirement" + }, + "type": [ + "object", + "null" + ] + }, + "exes": { + "items": { + "$ref": "#/definitions/ComputerUseWindowsExeConfig" + }, + "type": [ + "array", + "null" + ] + } + }, + "type": "object" + }, + "ComputerUseWindowsExeConfig": { + "properties": { + "access": { + "$ref": "#/definitions/AllowDenyRequirement" + }, + "binary_name": { + "type": [ + "string", + "null" + ] + }, + "product_name": { + "type": "string" + }, + "publisher_name": { + "type": "string" + } + }, + "required": [ + "access", + "product_name", + "publisher_name" + ], + "type": "object" + }, "Config": { "additionalProperties": true, "properties": { @@ -271,12 +450,32 @@ ], "description": "[UNSTABLE] Optional default for where approval requests are routed for review." }, + "browser_use": { + "anyOf": [ + { + "$ref": "#/definitions/BrowserUseConfig" + }, + { + "type": "null" + } + ] + }, "compact_prompt": { "type": [ "string", "null" ] }, + "computer_use": { + "anyOf": [ + { + "$ref": "#/definitions/ComputerUseConfig" + }, + { + "type": "null" + } + ] + }, "desktop": { "additionalProperties": true, "type": [ diff --git a/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-experimental.json.zst b/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-experimental.json.zst index a7dd6456fe..ddf91a3f82 100644 Binary files a/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-experimental.json.zst and b/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-experimental.json.zst differ diff --git a/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-stable.json.zst b/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-stable.json.zst index 97e4418b46..18a1bab62f 100644 Binary files a/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-stable.json.zst and b/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-stable.json.zst differ diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/BrowserUseConfig.ts b/codex-rs/app-server-protocol/schema/typescript/v2/BrowserUseConfig.ts new file mode 100644 index 0000000000..5cea3967e7 --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/BrowserUseConfig.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { BrowserUseOriginPolicyConfig } from "./BrowserUseOriginPolicyConfig"; + +export type BrowserUseConfig = { allow_history_access: boolean | null, default_origin_policy: BrowserUseOriginPolicyConfig | null, origins: { [key in string]?: BrowserUseOriginPolicyConfig } | null, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/BrowserUseOriginPolicyConfig.ts b/codex-rs/app-server-protocol/schema/typescript/v2/BrowserUseOriginPolicyConfig.ts new file mode 100644 index 0000000000..caf024ddc0 --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/BrowserUseOriginPolicyConfig.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AllowDenyRequirement } from "./AllowDenyRequirement"; + +export type BrowserUseOriginPolicyConfig = { access: AllowDenyRequirement | null, downloads: AllowDenyRequirement | null, uploads: AllowDenyRequirement | null, full_cdp_access: AllowDenyRequirement | null, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ComputerUseConfig.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ComputerUseConfig.ts new file mode 100644 index 0000000000..0069ddd471 --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ComputerUseConfig.ts @@ -0,0 +1,8 @@ +// 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 { AllowDenyRequirement } from "./AllowDenyRequirement"; +import type { ComputerUseMacosConfig } from "./ComputerUseMacosConfig"; +import type { ComputerUseWindowsConfig } from "./ComputerUseWindowsConfig"; + +export type ComputerUseConfig = { default_app_access: AllowDenyRequirement | null, macos: ComputerUseMacosConfig | null, windows: ComputerUseWindowsConfig | null, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ComputerUseMacosConfig.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ComputerUseMacosConfig.ts new file mode 100644 index 0000000000..91b03c30c9 --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ComputerUseMacosConfig.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AllowDenyRequirement } from "./AllowDenyRequirement"; + +export type ComputerUseMacosConfig = { bundle_ids: { [key in string]?: AllowDenyRequirement } | null, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ComputerUseWindowsConfig.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ComputerUseWindowsConfig.ts new file mode 100644 index 0000000000..9fe64efe95 --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ComputerUseWindowsConfig.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AllowDenyRequirement } from "./AllowDenyRequirement"; +import type { ComputerUseWindowsExeConfig } from "./ComputerUseWindowsExeConfig"; + +export type ComputerUseWindowsConfig = { aumids: { [key in string]?: AllowDenyRequirement } | null, exes: Array | null, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ComputerUseWindowsExeConfig.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ComputerUseWindowsExeConfig.ts new file mode 100644 index 0000000000..afd4ff225d --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ComputerUseWindowsExeConfig.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AllowDenyRequirement } from "./AllowDenyRequirement"; + +export type ComputerUseWindowsExeConfig = { publisher_name: string, product_name: string, binary_name: string | null, access: AllowDenyRequirement, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/Config.ts b/codex-rs/app-server-protocol/schema/typescript/v2/Config.ts index cc15fb4e72..4d0c409796 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/Config.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/Config.ts @@ -11,6 +11,8 @@ import type { JsonValue } from "../serde_json/JsonValue"; import type { AnalyticsConfig } from "./AnalyticsConfig"; import type { ApprovalsReviewer } from "./ApprovalsReviewer"; import type { AskForApproval } from "./AskForApproval"; +import type { BrowserUseConfig } from "./BrowserUseConfig"; +import type { ComputerUseConfig } from "./ComputerUseConfig"; import type { ForcedChatgptWorkspaceIds } from "./ForcedChatgptWorkspaceIds"; import type { SandboxMode } from "./SandboxMode"; import type { SandboxWorkspaceWrite } from "./SandboxWorkspaceWrite"; @@ -20,4 +22,4 @@ export type Config = {model: string | null, review_model: string | null, model_c * [UNSTABLE] Optional default for where approval requests are routed for * review. */ -approvals_reviewer: ApprovalsReviewer | null, sandbox_mode: SandboxMode | null, sandbox_workspace_write: SandboxWorkspaceWrite | null, forced_chatgpt_workspace_id: ForcedChatgptWorkspaceIds | null, forced_login_method: ForcedLoginMethod | null, web_search: WebSearchMode | null, tools: ToolsV2 | null, instructions: string | null, developer_instructions: string | null, compact_prompt: string | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, service_tier: string | null, analytics: AnalyticsConfig | null, desktop: { [key in string]?: JsonValue } | null} & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); +approvals_reviewer: ApprovalsReviewer | null, sandbox_mode: SandboxMode | null, sandbox_workspace_write: SandboxWorkspaceWrite | null, forced_chatgpt_workspace_id: ForcedChatgptWorkspaceIds | null, forced_login_method: ForcedLoginMethod | null, web_search: WebSearchMode | null, tools: ToolsV2 | null, instructions: string | null, developer_instructions: string | null, compact_prompt: string | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, service_tier: string | null, analytics: AnalyticsConfig | null, browser_use: BrowserUseConfig | null, computer_use: ComputerUseConfig | null, desktop: { [key in string]?: JsonValue } | null} & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/index.ts b/codex-rs/app-server-protocol/schema/typescript/v2/index.ts index 171d661412..2a1d8e7c27 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/index.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/index.ts @@ -45,7 +45,9 @@ export type { AttestationGenerateResponse } from "./AttestationGenerateResponse" export type { AutoReviewDecisionSource } from "./AutoReviewDecisionSource"; export type { AutoReviewRequirements } from "./AutoReviewRequirements"; export type { BrowserUseAccessApprovalLifetime } from "./BrowserUseAccessApprovalLifetime"; +export type { BrowserUseConfig } from "./BrowserUseConfig"; export type { BrowserUseOriginPolicy } from "./BrowserUseOriginPolicy"; +export type { BrowserUseOriginPolicyConfig } from "./BrowserUseOriginPolicyConfig"; export type { BrowserUseRequirements } from "./BrowserUseRequirements"; export type { ByteRange } from "./ByteRange"; export type { CancelLoginAccountParams } from "./CancelLoginAccountParams"; @@ -81,8 +83,12 @@ export type { CommandExecutionRequestApprovalResponse } from "./CommandExecution export type { CommandExecutionSource } from "./CommandExecutionSource"; export type { CommandExecutionStatus } from "./CommandExecutionStatus"; export type { CommandMigration } from "./CommandMigration"; +export type { ComputerUseConfig } from "./ComputerUseConfig"; +export type { ComputerUseMacosConfig } from "./ComputerUseMacosConfig"; export type { ComputerUseMacosRequirements } from "./ComputerUseMacosRequirements"; export type { ComputerUseRequirements } from "./ComputerUseRequirements"; +export type { ComputerUseWindowsConfig } from "./ComputerUseWindowsConfig"; +export type { ComputerUseWindowsExeConfig } from "./ComputerUseWindowsExeConfig"; export type { ComputerUseWindowsExeRequirement } from "./ComputerUseWindowsExeRequirement"; export type { ComputerUseWindowsRequirements } from "./ComputerUseWindowsRequirements"; export type { Config } from "./Config"; diff --git a/codex-rs/app-server-protocol/src/protocol/v2/browser_use_config.rs b/codex-rs/app-server-protocol/src/protocol/v2/browser_use_config.rs new file mode 100644 index 0000000000..0045f7b22a --- /dev/null +++ b/codex-rs/app-server-protocol/src/protocol/v2/browser_use_config.rs @@ -0,0 +1,25 @@ +use super::AllowDenyRequirement; +use crate::JsonSchema; +use crate::TS; +use serde::Deserialize; +use serde::Serialize; +use std::collections::BTreeMap; + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] +#[serde(rename_all = "snake_case")] +#[ts(export_to = "v2/")] +pub struct BrowserUseConfig { + pub allow_history_access: Option, + pub default_origin_policy: Option, + pub origins: Option>, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] +#[serde(rename_all = "snake_case")] +#[ts(export_to = "v2/")] +pub struct BrowserUseOriginPolicyConfig { + pub access: Option, + pub downloads: Option, + pub uploads: Option, + pub full_cdp_access: Option, +} diff --git a/codex-rs/app-server-protocol/src/protocol/v2/computer_use_config.rs b/codex-rs/app-server-protocol/src/protocol/v2/computer_use_config.rs new file mode 100644 index 0000000000..27399954f9 --- /dev/null +++ b/codex-rs/app-server-protocol/src/protocol/v2/computer_use_config.rs @@ -0,0 +1,40 @@ +use super::AllowDenyRequirement; +use crate::JsonSchema; +use crate::TS; +use serde::Deserialize; +use serde::Serialize; +use std::collections::BTreeMap; + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] +#[serde(rename_all = "snake_case")] +#[ts(export_to = "v2/")] +pub struct ComputerUseConfig { + pub default_app_access: Option, + pub macos: Option, + pub windows: Option, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] +#[serde(rename_all = "snake_case")] +#[ts(export_to = "v2/")] +pub struct ComputerUseMacosConfig { + pub bundle_ids: Option>, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] +#[serde(rename_all = "snake_case")] +#[ts(export_to = "v2/")] +pub struct ComputerUseWindowsConfig { + pub aumids: Option>, + pub exes: Option>, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] +#[serde(rename_all = "snake_case")] +#[ts(export_to = "v2/")] +pub struct ComputerUseWindowsExeConfig { + pub publisher_name: String, + pub product_name: String, + pub binary_name: Option, + pub access: AllowDenyRequirement, +} diff --git a/codex-rs/app-server-protocol/src/protocol/v2/config.rs b/codex-rs/app-server-protocol/src/protocol/v2/config.rs index 9c4b45774e..2e1fe1f564 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2/config.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2/config.rs @@ -1,5 +1,7 @@ use super::ApprovalsReviewer; use super::AskForApproval; +use super::BrowserUseConfig; +use super::ComputerUseConfig; use super::SandboxMode; use super::WindowsSandboxSetupMode; use super::shared::default_enabled; @@ -282,6 +284,8 @@ pub struct Config { #[experimental("config/read.apps")] #[serde(default)] pub apps: Option, + pub browser_use: Option, + pub computer_use: Option, pub desktop: Option>, #[serde(default, flatten)] pub additional: HashMap, diff --git a/codex-rs/app-server-protocol/src/protocol/v2/mod.rs b/codex-rs/app-server-protocol/src/protocol/v2/mod.rs index 97f8b053d9..75416d13a2 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2/mod.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2/mod.rs @@ -4,8 +4,10 @@ mod account; mod apps; mod attestation; mod bedrock; +mod browser_use_config; mod collaboration_mode; mod command_exec; +mod computer_use_config; mod config; mod current_time; mod diagnostics; @@ -36,8 +38,10 @@ pub use account::*; pub use apps::*; pub use attestation::*; pub use bedrock::*; +pub use browser_use_config::*; pub use collaboration_mode::*; pub use command_exec::*; +pub use computer_use_config::*; pub use config::*; pub use current_time::*; pub use diagnostics::*; diff --git a/codex-rs/app-server-protocol/src/protocol/v2/tests.rs b/codex-rs/app-server-protocol/src/protocol/v2/tests.rs index c71f65cbc2..20b5ac3b46 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2/tests.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2/tests.rs @@ -1996,6 +1996,8 @@ fn config_granular_approval_policy_is_marked_experimental() { service_tier: None, analytics: None, apps: None, + browser_use: None, + computer_use: None, desktop: None, additional: HashMap::new(), }); @@ -2029,6 +2031,8 @@ fn config_approvals_reviewer_is_marked_experimental() { service_tier: None, analytics: None, apps: None, + browser_use: None, + computer_use: None, desktop: None, additional: HashMap::new(), }); diff --git a/codex-rs/app-server/tests/suite/v2/config_rpc.rs b/codex-rs/app-server/tests/suite/v2/config_rpc.rs index ac95494782..3f681f7336 100644 --- a/codex-rs/app-server/tests/suite/v2/config_rpc.rs +++ b/codex-rs/app-server/tests/suite/v2/config_rpc.rs @@ -10,11 +10,17 @@ use codex_app_server_protocol::AppsConfig; use codex_app_server_protocol::AppsDefaultConfig; use codex_app_server_protocol::AskForApproval; use codex_app_server_protocol::BrowserUseAccessApprovalLifetime; +use codex_app_server_protocol::BrowserUseConfig; use codex_app_server_protocol::BrowserUseOriginPolicy; +use codex_app_server_protocol::BrowserUseOriginPolicyConfig; use codex_app_server_protocol::BrowserUseRequirements; use codex_app_server_protocol::CliAuthCredentialsStoreMode; +use codex_app_server_protocol::ComputerUseConfig; +use codex_app_server_protocol::ComputerUseMacosConfig; use codex_app_server_protocol::ComputerUseMacosRequirements; use codex_app_server_protocol::ComputerUseRequirements; +use codex_app_server_protocol::ComputerUseWindowsConfig; +use codex_app_server_protocol::ComputerUseWindowsExeConfig; use codex_app_server_protocol::ComputerUseWindowsExeRequirement; use codex_app_server_protocol::ComputerUseWindowsRequirements; use codex_app_server_protocol::ConfigBatchWriteParams; @@ -613,6 +619,236 @@ allowed_domains = ["example.com"] Ok(()) } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn config_read_includes_browser_and_computer_use_config() -> Result<()> { + let codex_home = TempDir::new()?; + write_config( + &codex_home, + r#" +[browser_use] +allow_history_access = true + +[browser_use.default_origin_policy] +access = "deny" +downloads = "allow" + +[browser_use.origins."https://example.com"] +downloads = "deny" +uploads = "allow" + +[computer_use] +default_app_access = "deny" + +[computer_use.macos.bundle_ids] +"com.apple.Safari" = "allow" + +[computer_use.windows.aumids] +"Microsoft.Paint_8wekyb3d8bbwe!App" = "deny" + +[[computer_use.windows.exes]] +publisher_name = "CN=Google LLC" +product_name = "Google Chrome" +binary_name = "chrome.exe" +access = "allow" +"#, + )?; + std::fs::write( + codex_home.path().join("requirements.toml"), + r#" +allow_browser_and_computer_use = false + +[browser_use] +allow_history_access = false +allow_global_persistent_approval = false + +[browser_use.default_origin_policy] +access = "allow" + +[computer_use] +default_app_access = "allow" +"#, + )?; + + let workspace = TempDir::new()?; + let project_config_dir = workspace.path().join(".codex"); + std::fs::create_dir_all(&project_config_dir)?; + std::fs::write( + project_config_dir.join("config.toml"), + r#" +[browser_use.default_origin_policy] +uploads = "deny" +full_cdp_access = "allow" + +[browser_use.origins."https://example.com"] +access = "allow" +full_cdp_access = "deny" + +[computer_use.macos.bundle_ids] +"com.apple.TextEdit" = "allow" +"com.apple.Safari" = "deny" + +[computer_use.windows.aumids] +"Microsoft.WindowsCalculator_8wekyb3d8bbwe!App" = "allow" + +[[computer_use.windows.exes]] +publisher_name = "CN=Microsoft Corporation" +product_name = "Microsoft Visual Studio Code" +access = "deny" +"#, + )?; + set_project_trust_level(codex_home.path(), workspace.path(), TrustLevel::Trusted)?; + let codex_home_path = codex_home.path().canonicalize()?; + let user_file = AbsolutePathBuf::try_from(codex_home_path.join("config.toml"))?; + let project_config = AbsolutePathBuf::try_from(project_config_dir)?; + + let mut mcp = TestAppServer::builder() + .with_codex_home(codex_home.path()) + .without_auto_env() + .build_initialized_with_timeout(DEFAULT_READ_TIMEOUT) + .await?; + + let request_id = mcp + .send_config_read_request(ConfigReadParams { + include_layers: false, + cwd: Some(workspace.path().to_string_lossy().into_owned()), + }) + .await?; + let ConfigReadResponse { + config, origins, .. + } = timeout(DEFAULT_READ_TIMEOUT, mcp.read_response(request_id)).await??; + + assert_eq!( + config.browser_use, + Some(BrowserUseConfig { + allow_history_access: Some(true), + default_origin_policy: Some(BrowserUseOriginPolicyConfig { + access: Some(AllowDenyRequirement::Deny), + downloads: Some(AllowDenyRequirement::Allow), + uploads: Some(AllowDenyRequirement::Deny), + full_cdp_access: Some(AllowDenyRequirement::Allow), + }), + origins: Some(BTreeMap::from([( + "https://example.com".to_string(), + BrowserUseOriginPolicyConfig { + access: Some(AllowDenyRequirement::Allow), + downloads: Some(AllowDenyRequirement::Deny), + uploads: Some(AllowDenyRequirement::Allow), + full_cdp_access: Some(AllowDenyRequirement::Deny), + }, + )])), + }) + ); + assert_eq!( + config.computer_use, + Some(ComputerUseConfig { + default_app_access: Some(AllowDenyRequirement::Deny), + macos: Some(ComputerUseMacosConfig { + bundle_ids: Some(BTreeMap::from([ + ("com.apple.Safari".to_string(), AllowDenyRequirement::Deny,), + ( + "com.apple.TextEdit".to_string(), + AllowDenyRequirement::Allow, + ), + ])), + }), + windows: Some(ComputerUseWindowsConfig { + aumids: Some(BTreeMap::from([ + ( + "Microsoft.Paint_8wekyb3d8bbwe!App".to_string(), + AllowDenyRequirement::Deny, + ), + ( + "Microsoft.WindowsCalculator_8wekyb3d8bbwe!App".to_string(), + AllowDenyRequirement::Allow, + ), + ])), + exes: Some(vec![ComputerUseWindowsExeConfig { + publisher_name: "CN=Microsoft Corporation".to_string(), + product_name: "Microsoft Visual Studio Code".to_string(), + binary_name: None, + access: AllowDenyRequirement::Deny, + }]), + }), + }) + ); + assert_eq!( + origins + .get("browser_use.default_origin_policy.access") + .expect("user policy origin") + .name, + ConfigLayerSource::User { + file: user_file.clone(), + profile: None, + } + ); + assert_eq!( + origins + .get("browser_use.default_origin_policy.full_cdp_access") + .expect("project policy origin") + .name, + ConfigLayerSource::Project { + dot_codex_folder: project_config.clone(), + } + ); + assert_eq!( + origins + .get("browser_use.allow_history_access") + .expect("user browser use origin") + .name, + ConfigLayerSource::User { + file: user_file, + profile: None, + } + ); + assert_eq!( + origins + .get("computer_use.windows.exes.0.access") + .expect("project computer use origin") + .name, + ConfigLayerSource::Project { + dot_codex_folder: project_config, + } + ); + + let request_id = mcp.send_config_requirements_read_request().await?; + let response: ConfigRequirementsReadResponse = + timeout(DEFAULT_READ_TIMEOUT, mcp.read_response(request_id)).await??; + let requirements = response + .requirements + .expect("managed requirements should be returned"); + assert_eq!(requirements.allow_browser_and_computer_use, Some(false)); + assert_eq!( + requirements.browser_use, + Some(BrowserUseRequirements { + allow_history_access: Some(false), + disable_auto_review: None, + allow_global_persistent_approval: Some(false), + default_origin_policy: Some(BrowserUseOriginPolicy { + access: Some(AllowDenyRequirement::Allow), + downloads: None, + uploads: None, + full_cdp_access: None, + auto_review: None, + persistent_approval: None, + access_approval_lifetime: None, + }), + origins: None, + }) + ); + assert_eq!( + requirements.computer_use, + Some(ComputerUseRequirements { + allow_locked_computer_use: None, + allow_persistent_approval: None, + default_app_access: Some(AllowDenyRequirement::Allow), + macos: None, + windows: None, + }) + ); + + Ok(()) +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn config_read_accepts_legacy_forced_chatgpt_workspace_id() -> Result<()> { const WORKSPACE_ID: &str = "123e4567-e89b-42d3-a456-426614174000"; @@ -1422,6 +1658,238 @@ async fn config_batch_write_applies_multiple_edits() -> Result<()> { Ok(()) } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn config_batch_write_round_trips_browser_and_computer_use_config() -> Result<()> { + let tmp_dir = TempDir::new()?; + let codex_home = tmp_dir.path().canonicalize()?; + write_config( + &tmp_dir, + r#" +model = "gpt-existing" + +[browser_use.origins."https://stale.example"] +access = "deny" + +[computer_use.macos.bundle_ids] +"com.example.Stale" = "deny" + +[computer_use.windows.aumids] +"Stale.App_123!App" = "allow" + +[[computer_use.windows.exes]] +publisher_name = "CN=Stale" +product_name = "Stale App" +binary_name = "stale.exe" +access = "deny" +"#, + )?; + + let mut mcp = TestAppServer::builder() + .with_codex_home(&codex_home) + .without_auto_env() + .build_initialized_with_timeout(DEFAULT_READ_TIMEOUT) + .await?; + + let batch_id = mcp + .send_config_batch_write_request(ConfigBatchWriteParams { + file_path: None, + edits: vec![ + ConfigEdit { + key_path: "browser_use.allow_history_access".to_string(), + value: json!(true), + merge_strategy: MergeStrategy::Replace, + }, + ConfigEdit { + key_path: "browser_use.default_origin_policy".to_string(), + value: json!({ + "access": "allow", + "downloads": "deny", + "uploads": "allow", + "full_cdp_access": "deny", + }), + merge_strategy: MergeStrategy::Replace, + }, + ConfigEdit { + key_path: "browser_use.origins.\"https://example.com\"".to_string(), + value: json!({ + "access": "deny", + "downloads": "allow", + "uploads": "deny", + "full_cdp_access": "allow", + }), + merge_strategy: MergeStrategy::Replace, + }, + ConfigEdit { + key_path: "browser_use.origins.\"https://stale.example\"".to_string(), + value: json!(null), + merge_strategy: MergeStrategy::Replace, + }, + ConfigEdit { + key_path: "computer_use.default_app_access".to_string(), + value: json!("deny"), + merge_strategy: MergeStrategy::Replace, + }, + ConfigEdit { + key_path: "computer_use.macos.bundle_ids.\"com.apple.Safari\"".to_string(), + value: json!("allow"), + merge_strategy: MergeStrategy::Replace, + }, + ConfigEdit { + key_path: "computer_use.macos.bundle_ids.\"com.example.Stale\"".to_string(), + value: json!(null), + merge_strategy: MergeStrategy::Replace, + }, + ConfigEdit { + key_path: "computer_use.windows.aumids.\"Microsoft.Paint_8wekyb3d8bbwe!App\"" + .to_string(), + value: json!("deny"), + merge_strategy: MergeStrategy::Replace, + }, + ConfigEdit { + key_path: "computer_use.windows.aumids.\"Stale.App_123!App\"".to_string(), + value: json!(null), + merge_strategy: MergeStrategy::Replace, + }, + ConfigEdit { + key_path: "computer_use.windows.exes".to_string(), + value: json!([ + { + "publisher_name": "CN=Google LLC", + "product_name": "Google Chrome", + "binary_name": "chrome.exe", + "access": "allow", + }, + { + "publisher_name": "CN=Microsoft Corporation", + "product_name": "Microsoft Visual Studio Code", + "access": "deny", + }, + ]), + merge_strategy: MergeStrategy::Replace, + }, + ], + expected_version: None, + reload_user_config: false, + }) + .await?; + let batch_write: ConfigWriteResponse = + timeout(DEFAULT_READ_TIMEOUT, mcp.read_response(batch_id)).await??; + assert_eq!(batch_write.status, WriteStatus::Ok); + assert_eq!( + batch_write.file_path, + AbsolutePathBuf::resolve_path_against_base("config.toml", &codex_home) + ); + assert_eq!(batch_write.overridden_metadata, None); + + let persisted: toml::Value = + toml::from_str(&std::fs::read_to_string(codex_home.join("config.toml"))?)?; + let expected_persisted: toml::Value = toml::from_str( + r#" +model = "gpt-existing" + +[browser_use] +allow_history_access = true + +[browser_use.default_origin_policy] +access = "allow" +downloads = "deny" +uploads = "allow" +full_cdp_access = "deny" + +[browser_use.origins."https://example.com"] +access = "deny" +downloads = "allow" +uploads = "deny" +full_cdp_access = "allow" + +[computer_use] +default_app_access = "deny" + +[computer_use.macos.bundle_ids] +"com.apple.Safari" = "allow" + +[computer_use.windows.aumids] +"Microsoft.Paint_8wekyb3d8bbwe!App" = "deny" + +[[computer_use.windows.exes]] +publisher_name = "CN=Google LLC" +product_name = "Google Chrome" +binary_name = "chrome.exe" +access = "allow" + +[[computer_use.windows.exes]] +publisher_name = "CN=Microsoft Corporation" +product_name = "Microsoft Visual Studio Code" +access = "deny" +"#, + )?; + assert_eq!(persisted, expected_persisted); + + let read_id = mcp + .send_config_read_request(ConfigReadParams { + include_layers: false, + cwd: None, + }) + .await?; + let read: ConfigReadResponse = + timeout(DEFAULT_READ_TIMEOUT, mcp.read_response(read_id)).await??; + assert_eq!( + read.config.browser_use, + Some(BrowserUseConfig { + allow_history_access: Some(true), + default_origin_policy: Some(BrowserUseOriginPolicyConfig { + access: Some(AllowDenyRequirement::Allow), + downloads: Some(AllowDenyRequirement::Deny), + uploads: Some(AllowDenyRequirement::Allow), + full_cdp_access: Some(AllowDenyRequirement::Deny), + }), + origins: Some(BTreeMap::from([( + "https://example.com".to_string(), + BrowserUseOriginPolicyConfig { + access: Some(AllowDenyRequirement::Deny), + downloads: Some(AllowDenyRequirement::Allow), + uploads: Some(AllowDenyRequirement::Deny), + full_cdp_access: Some(AllowDenyRequirement::Allow), + }, + )])), + }) + ); + assert_eq!( + read.config.computer_use, + Some(ComputerUseConfig { + default_app_access: Some(AllowDenyRequirement::Deny), + macos: Some(ComputerUseMacosConfig { + bundle_ids: Some(BTreeMap::from([( + "com.apple.Safari".to_string(), + AllowDenyRequirement::Allow, + )])), + }), + windows: Some(ComputerUseWindowsConfig { + aumids: Some(BTreeMap::from([( + "Microsoft.Paint_8wekyb3d8bbwe!App".to_string(), + AllowDenyRequirement::Deny, + )])), + exes: Some(vec![ + ComputerUseWindowsExeConfig { + publisher_name: "CN=Google LLC".to_string(), + product_name: "Google Chrome".to_string(), + binary_name: Some("chrome.exe".to_string()), + access: AllowDenyRequirement::Allow, + }, + ComputerUseWindowsExeConfig { + publisher_name: "CN=Microsoft Corporation".to_string(), + product_name: "Microsoft Visual Studio Code".to_string(), + binary_name: None, + access: AllowDenyRequirement::Deny, + }, + ]), + }), + }) + ); + + Ok(()) +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn config_batch_write_rejects_legacy_profile_tables() -> Result<()> { let tmp_dir = TempDir::new()?; diff --git a/codex-rs/config/src/browser_computer_use_requirements.rs b/codex-rs/config/src/browser_computer_use_requirements.rs index eaf545a28a..93cb869d41 100644 --- a/codex-rs/config/src/browser_computer_use_requirements.rs +++ b/codex-rs/config/src/browser_computer_use_requirements.rs @@ -1,7 +1,9 @@ +use schemars::JsonSchema; use serde::Deserialize; +use serde::Serialize; use std::collections::BTreeMap; -#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "lowercase")] pub enum AllowDenyRequirementToml { Allow, diff --git a/codex-rs/config/src/browser_use.rs b/codex-rs/config/src/browser_use.rs new file mode 100644 index 0000000000..aa6a314d7e --- /dev/null +++ b/codex-rs/config/src/browser_use.rs @@ -0,0 +1,26 @@ +use crate::AllowDenyRequirementToml; +use schemars::JsonSchema; +use serde::Deserialize; +use serde::Serialize; +use std::collections::BTreeMap; + +#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, JsonSchema)] +#[schemars(deny_unknown_fields)] +pub struct BrowserUseConfigToml { + pub allow_history_access: Option, + pub default_origin_policy: Option, + pub origins: Option>, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, JsonSchema)] +#[schemars(deny_unknown_fields)] +pub struct BrowserUseOriginPolicyConfigToml { + pub access: Option, + pub downloads: Option, + pub uploads: Option, + pub full_cdp_access: Option, +} + +#[cfg(test)] +#[path = "browser_use_tests.rs"] +mod tests; diff --git a/codex-rs/config/src/browser_use_tests.rs b/codex-rs/config/src/browser_use_tests.rs new file mode 100644 index 0000000000..d36ef85820 --- /dev/null +++ b/codex-rs/config/src/browser_use_tests.rs @@ -0,0 +1,52 @@ +use super::*; +use crate::AllowDenyRequirementToml; +use crate::config_toml::ConfigToml; +use pretty_assertions::assert_eq; + +#[test] +fn browser_use_origin_policies_round_trip() { + let config: ConfigToml = toml::from_str( + r#" +[browser_use] +allow_history_access = true + +[browser_use.default_origin_policy] +access = "deny" +downloads = "allow" +uploads = "deny" +full_cdp_access = "allow" + +[browser_use.origins."https://example.com"] +access = "allow" +downloads = "deny" +uploads = "allow" +full_cdp_access = "deny" +"#, + ) + .expect("browser use config should deserialize"); + + let expected = BrowserUseConfigToml { + allow_history_access: Some(true), + default_origin_policy: Some(BrowserUseOriginPolicyConfigToml { + access: Some(AllowDenyRequirementToml::Deny), + downloads: Some(AllowDenyRequirementToml::Allow), + uploads: Some(AllowDenyRequirementToml::Deny), + full_cdp_access: Some(AllowDenyRequirementToml::Allow), + }), + origins: Some(BTreeMap::from([( + "https://example.com".to_string(), + BrowserUseOriginPolicyConfigToml { + access: Some(AllowDenyRequirementToml::Allow), + downloads: Some(AllowDenyRequirementToml::Deny), + uploads: Some(AllowDenyRequirementToml::Allow), + full_cdp_access: Some(AllowDenyRequirementToml::Deny), + }, + )])), + }; + assert_eq!(config.browser_use, Some(expected.clone())); + + let serialized = toml::to_string(&config).expect("browser use config should serialize"); + let reparsed: ConfigToml = + toml::from_str(&serialized).expect("serialized browser use config should deserialize"); + assert_eq!(reparsed.browser_use, Some(expected)); +} diff --git a/codex-rs/config/src/computer_use.rs b/codex-rs/config/src/computer_use.rs new file mode 100644 index 0000000000..6523d19bed --- /dev/null +++ b/codex-rs/config/src/computer_use.rs @@ -0,0 +1,39 @@ +use crate::AllowDenyRequirementToml; +use schemars::JsonSchema; +use serde::Deserialize; +use serde::Serialize; +use std::collections::BTreeMap; + +#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, JsonSchema)] +#[schemars(deny_unknown_fields)] +pub struct ComputerUseConfigToml { + pub default_app_access: Option, + pub macos: Option, + pub windows: Option, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, JsonSchema)] +#[schemars(deny_unknown_fields)] +pub struct ComputerUseMacosConfigToml { + pub bundle_ids: Option>, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, JsonSchema)] +#[schemars(deny_unknown_fields)] +pub struct ComputerUseWindowsConfigToml { + pub aumids: Option>, + pub exes: Option>, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema)] +#[schemars(deny_unknown_fields)] +pub struct ComputerUseWindowsExeConfigToml { + pub publisher_name: String, + pub product_name: String, + pub binary_name: Option, + pub access: AllowDenyRequirementToml, +} + +#[cfg(test)] +#[path = "computer_use_tests.rs"] +mod tests; diff --git a/codex-rs/config/src/computer_use_tests.rs b/codex-rs/config/src/computer_use_tests.rs new file mode 100644 index 0000000000..96505c4388 --- /dev/null +++ b/codex-rs/config/src/computer_use_tests.rs @@ -0,0 +1,54 @@ +use super::*; +use crate::config_toml::ConfigToml; +use pretty_assertions::assert_eq; + +#[test] +fn computer_use_config_round_trips() { + let config: ConfigToml = toml::from_str( + r#" +[computer_use] +default_app_access = "deny" + +[computer_use.macos.bundle_ids] +"com.apple.Safari" = "allow" + +[computer_use.windows.aumids] +"Microsoft.Paint_8wekyb3d8bbwe!App" = "deny" + +[[computer_use.windows.exes]] +publisher_name = "CN=Google LLC" +product_name = "Google Chrome" +binary_name = "chrome.exe" +access = "allow" +"#, + ) + .expect("computer use config should deserialize"); + + let expected = ComputerUseConfigToml { + default_app_access: Some(AllowDenyRequirementToml::Deny), + macos: Some(ComputerUseMacosConfigToml { + bundle_ids: Some(BTreeMap::from([( + "com.apple.Safari".to_string(), + AllowDenyRequirementToml::Allow, + )])), + }), + windows: Some(ComputerUseWindowsConfigToml { + aumids: Some(BTreeMap::from([( + "Microsoft.Paint_8wekyb3d8bbwe!App".to_string(), + AllowDenyRequirementToml::Deny, + )])), + exes: Some(vec![ComputerUseWindowsExeConfigToml { + publisher_name: "CN=Google LLC".to_string(), + product_name: "Google Chrome".to_string(), + binary_name: Some("chrome.exe".to_string()), + access: AllowDenyRequirementToml::Allow, + }]), + }), + }; + assert_eq!(config.computer_use, Some(expected.clone())); + + let serialized = toml::to_string(&config).expect("computer use config should serialize"); + let reparsed: ConfigToml = + toml::from_str(&serialized).expect("serialized computer use config should deserialize"); + assert_eq!(reparsed.computer_use, Some(expected)); +} diff --git a/codex-rs/config/src/config_toml.rs b/codex-rs/config/src/config_toml.rs index ceff237fe8..af5df4f1fc 100644 --- a/codex-rs/config/src/config_toml.rs +++ b/codex-rs/config/src/config_toml.rs @@ -6,6 +6,8 @@ use std::num::NonZeroU64; use std::path::Path; use crate::HooksToml; +use crate::browser_use::BrowserUseConfigToml; +use crate::computer_use::ComputerUseConfigToml; use crate::permissions_toml::PermissionsToml; use crate::profile_toml::ConfigProfile; use crate::types::AnalyticsConfigToml; @@ -182,6 +184,10 @@ pub struct ConfigToml { #[serde(default)] pub auto_review: Option, + pub browser_use: Option, + + pub computer_use: Option, + #[serde(default)] pub shell_environment_policy: ShellEnvironmentPolicyToml, diff --git a/codex-rs/config/src/lib.rs b/codex-rs/config/src/lib.rs index 1913d09058..e441ea4578 100644 --- a/codex-rs/config/src/lib.rs +++ b/codex-rs/config/src/lib.rs @@ -1,7 +1,9 @@ mod auth_policy; mod browser_computer_use_requirements; +mod browser_use; mod cloud_config_bundle; mod cloud_config_layers; +mod computer_use; mod config_layer_source; mod config_requirements; pub mod config_toml; @@ -46,6 +48,8 @@ pub use browser_computer_use_requirements::ComputerUseMacosRequirementsToml; pub use browser_computer_use_requirements::ComputerUseRequirementsToml; pub use browser_computer_use_requirements::ComputerUseWindowsExeRequirementToml; pub use browser_computer_use_requirements::ComputerUseWindowsRequirementsToml; +pub use browser_use::BrowserUseConfigToml; +pub use browser_use::BrowserUseOriginPolicyConfigToml; pub use cloud_config_bundle::CloudConfigBundle; pub use cloud_config_bundle::CloudConfigBundleLayers; pub use cloud_config_bundle::CloudConfigBundleLoadError; @@ -69,6 +73,10 @@ pub use codex_protocol::mcp_policy::McpServerValueMatcher; pub use codex_protocol::mcp_policy::PluginMcpRequirements as PluginRequirementsToml; pub use codex_utils_absolute_path::AbsolutePathBuf; pub use codex_utils_absolute_path::AbsolutePathBufGuard; +pub use computer_use::ComputerUseConfigToml; +pub use computer_use::ComputerUseMacosConfigToml; +pub use computer_use::ComputerUseWindowsConfigToml; +pub use computer_use::ComputerUseWindowsExeConfigToml; pub use config_layer_source::ConfigLayer; pub use config_layer_source::ConfigLayerMetadata; pub use config_layer_source::ConfigLayerSource; diff --git a/codex-rs/core/config.schema.json b/codex-rs/core/config.schema.json index ea0fe82f37..8e124a5299 100644 --- a/codex-rs/core/config.schema.json +++ b/codex-rs/core/config.schema.json @@ -70,6 +70,13 @@ }, "type": "object" }, + "AllowDenyRequirementToml": { + "enum": [ + "allow", + "deny" + ], + "type": "string" + }, "AltScreenMode": { "description": "Controls whether the TUI uses the terminal's alternate screen buffer.\n\n- `auto` (default): Use alternate screen mode. - `always`: Always use alternate screen mode. - `never`: Never use alternate screen mode. Runs in inline mode, preserving scrollback.\n\nThe CLI flag `--no-alt-screen` can override this setting at runtime.", "oneOf": [ @@ -374,6 +381,42 @@ ], "type": "object" }, + "BrowserUseConfigToml": { + "additionalProperties": false, + "properties": { + "allow_history_access": { + "type": "boolean" + }, + "default_origin_policy": { + "$ref": "#/definitions/BrowserUseOriginPolicyConfigToml" + }, + "origins": { + "additionalProperties": { + "$ref": "#/definitions/BrowserUseOriginPolicyConfigToml" + }, + "type": "object" + } + }, + "type": "object" + }, + "BrowserUseOriginPolicyConfigToml": { + "additionalProperties": false, + "properties": { + "access": { + "$ref": "#/definitions/AllowDenyRequirementToml" + }, + "downloads": { + "$ref": "#/definitions/AllowDenyRequirementToml" + }, + "full_cdp_access": { + "$ref": "#/definitions/AllowDenyRequirementToml" + }, + "uploads": { + "$ref": "#/definitions/AllowDenyRequirementToml" + } + }, + "type": "object" + }, "BundledSkillsConfig": { "additionalProperties": false, "properties": { @@ -426,6 +469,74 @@ }, "type": "object" }, + "ComputerUseConfigToml": { + "additionalProperties": false, + "properties": { + "default_app_access": { + "$ref": "#/definitions/AllowDenyRequirementToml" + }, + "macos": { + "$ref": "#/definitions/ComputerUseMacosConfigToml" + }, + "windows": { + "$ref": "#/definitions/ComputerUseWindowsConfigToml" + } + }, + "type": "object" + }, + "ComputerUseMacosConfigToml": { + "additionalProperties": false, + "properties": { + "bundle_ids": { + "additionalProperties": { + "$ref": "#/definitions/AllowDenyRequirementToml" + }, + "type": "object" + } + }, + "type": "object" + }, + "ComputerUseWindowsConfigToml": { + "additionalProperties": false, + "properties": { + "aumids": { + "additionalProperties": { + "$ref": "#/definitions/AllowDenyRequirementToml" + }, + "type": "object" + }, + "exes": { + "items": { + "$ref": "#/definitions/ComputerUseWindowsExeConfigToml" + }, + "type": "array" + } + }, + "type": "object" + }, + "ComputerUseWindowsExeConfigToml": { + "additionalProperties": false, + "properties": { + "access": { + "$ref": "#/definitions/AllowDenyRequirementToml" + }, + "binary_name": { + "type": "string" + }, + "product_name": { + "type": "string" + }, + "publisher_name": { + "type": "string" + } + }, + "required": [ + "access", + "product_name", + "publisher_name" + ], + "type": "object" + }, "ConfigProfile": { "additionalProperties": false, "description": "Collection of common configuration options that a user can define as a unit in `config.toml`.", @@ -5334,6 +5445,9 @@ "minimum": 0.0, "type": "integer" }, + "browser_use": { + "$ref": "#/definitions/BrowserUseConfigToml" + }, "chatgpt_base_url": { "description": "Base URL for requests to ChatGPT (as opposed to the OpenAI API).", "type": "string" @@ -5355,6 +5469,9 @@ "description": "Compact prompt used for history compaction.", "type": "string" }, + "computer_use": { + "$ref": "#/definitions/ComputerUseConfigToml" + }, "default_permissions": { "description": "Default permissions profile to apply. Names starting with `:` refer to built-in profiles; other names are resolved from the `[permissions]` table.", "type": "string"