diff --git a/codex-rs/app-server/README.md b/codex-rs/app-server/README.md index 42ac510a8a..6db159dc02 100644 --- a/codex-rs/app-server/README.md +++ b/codex-rs/app-server/README.md @@ -236,6 +236,37 @@ Example with notification opt-out: - `config/batchWrite` — apply multiple config edits atomically to the user's config.toml on disk, with optional `reloadUserConfig: true` to hot-reload loaded threads, including multiple `desktop.*` edits. - `configRequirements/read` — fetch loaded requirements constraints from `requirements.toml` and/or MDM (or `null` if none are configured), including allow-lists (`allowedApprovalPolicies`, `allowedSandboxModes`, `allowedWebSearchModes`), the layered permission-profile allow map (`allowedPermissionProfiles`), the managed permission-profile default (`defaultPermissions`), lifecycle hook lockdown (`allowManagedHooksOnly`), computer use policy (`computerUse`), pinned feature values (`featureRequirements`), managed lifecycle hooks (`hooks`), `enforceResidency`, and `network` constraints such as canonical domain/socket permissions plus `managedAllowedDomainsOnly` and `dangerFullAccessDenylistOnly`. +### Example: Supply an account-scoped feature default + +The Codex App can evaluate a Statsig gate and pass the resulting default to Rust without adding a +Statsig client to `codex-rs`. For example, the App can target personal accounts with `cdp = true` +and workspace accounts with `cdp = false`, then send the selected value after app-server +initialization: + +```json +{ + "method": "experimentalFeature/enablement/set", + "id": 20, + "params": { + "enablement": { + "cdp": true + } + } +} +``` + +This value is an in-memory default, not an administrator policy. An explicit `features.cdp` value +in `config.toml` takes precedence, and a cloud-delivered requirement can pin the value for managed +accounts: + +```toml +[features] +cdp = false +``` + +The persistent personal-account opt-in/opt-out experience is intentionally outside this example; +it needs a separate product decision about where that account preference is stored and synchronized. + ### Example: Start or resume a thread Start a fresh thread when you need a new Codex conversation. diff --git a/codex-rs/app-server/src/request_processors/config_processor.rs b/codex-rs/app-server/src/request_processors/config_processor.rs index bdc8013950..01e54d25eb 100644 --- a/codex-rs/app-server/src/request_processors/config_processor.rs +++ b/codex-rs/app-server/src/request_processors/config_processor.rs @@ -47,6 +47,7 @@ use std::path::PathBuf; const SUPPORTED_EXPERIMENTAL_FEATURE_ENABLEMENT: &[&str] = &[ "auth_elicitation", + "cdp", "memories", "mentions_v2", "remote_control", diff --git a/codex-rs/features/src/lib.rs b/codex-rs/features/src/lib.rs index 2f8e91d709..66c205b5ec 100644 --- a/codex-rs/features/src/lib.rs +++ b/codex-rs/features/src/lib.rs @@ -127,6 +127,11 @@ pub enum Feature { LocalThreadStoreCompression, /// Enable the Chronicle sidecar for passive screen-context memories. Chronicle, + /// Enable CDP. + /// + /// The Codex App supplies the account-scoped rollout default at runtime. + /// Explicit config and managed requirements retain higher precedence. + Cdp, /// Append additional AGENTS.md guidance to user instructions. ChildAgentsMd, /// Compress request bodies (zstd) when sending streaming requests to codex-backend. @@ -870,6 +875,15 @@ pub const FEATURES: &[FeatureSpec] = &[ stage: Stage::UnderDevelopment, default_enabled: false, }, + FeatureSpec { + id: Feature::Cdp, + key: "cdp", + stage: Stage::UnderDevelopment, + // The App supplies the account-scoped default through + // `experimentalFeature/enablement/set`. Keep the Rust fallback off for + // clients that do not evaluate the rollout. + default_enabled: false, + }, FeatureSpec { id: Feature::ChildAgentsMd, key: "child_agents_md",