//! Types used to define loaded and effective Codex configuration values.// Note this file should generally be restricted to simple struct/enum// definitions that do not contain business logic.pub use crate::mcp_types::AppToolApproval;pub use crate::mcp_types::McpServerConfig;pub use crate::mcp_types::McpServerDisabledReason;pub use crate::mcp_types::McpServerEnvVar;pub use crate::mcp_types::McpServerOAuthConfig;pub use crate::mcp_types::McpServerToolConfig;pub use crate::mcp_types::McpServerTransportConfig;pub use crate::mcp_types::RawMcpServerConfig;pub use codex_protocol::config_types::AltScreenMode;pub use codex_protocol::config_types::ApprovalsReviewer;use codex_protocol::config_types::EnvironmentVariablePattern;pub use codex_protocol::config_types::ModeKind;pub use codex_protocol::config_types::Personality;pub use codex_protocol::config_types::ServiceTier;use codex_protocol::config_types::ShellEnvironmentPolicy;use codex_protocol::config_types::ShellEnvironmentPolicyInherit;pub use codex_protocol::config_types::WebSearchMode;use codex_utils_absolute_path::AbsolutePathBuf;use std::collections::BTreeMap;use std::collections::HashMap;use std::fmt;use schemars::JsonSchema;use serde::Deserialize;use serde::Serialize;pub use crate::tui_keymap::KeybindingSpec;pub use crate::tui_keymap::KeybindingsSpec;pub use crate::tui_keymap::MAX_FUNCTION_KEY;pub use crate::tui_keymap::TuiApprovalKeymap;pub use crate::tui_keymap::TuiChatKeymap;pub use crate::tui_keymap::TuiComposerKeymap;pub use crate::tui_keymap::TuiEditorKeymap;pub use crate::tui_keymap::TuiGlobalKeymap;pub use crate::tui_keymap::TuiKeymap;pub use crate::tui_keymap::TuiListKeymap;pub use crate::tui_keymap::TuiPagerKeymap;pub use crate::tui_keymap::TuiVimNormalKeymap;pub use crate::tui_keymap::TuiVimOperatorKeymap;pub const DEFAULT_OTEL_ENVIRONMENT: &str = "dev";pub const DEFAULT_MEMORIES_MAX_ROLLOUTS_PER_STARTUP: usize = 2;pub const DEFAULT_MEMORIES_MAX_ROLLOUT_AGE_DAYS: i64 = 10;pub const DEFAULT_MEMORIES_MIN_ROLLOUT_IDLE_HOURS: i64 = 6;pub const DEFAULT_MEMORIES_MIN_RATE_LIMIT_REMAINING_PERCENT: i64 = 25;pub const DEFAULT_MEMORIES_MAX_RAW_MEMORIES_FOR_CONSOLIDATION: usize = 256;pub const DEFAULT_MEMORIES_MAX_UNUSED_DAYS: i64 = 30;const MIN_MEMORIES_MAX_RAW_MEMORIES_FOR_CONSOLIDATION: usize = 1;const MAX_MEMORIES_MAX_RAW_MEMORIES_FOR_CONSOLIDATION: usize = 4096;const MIN_MEMORIES_MAX_ROLLOUTS_PER_STARTUP: usize = 1;const MAX_MEMORIES_MAX_ROLLOUTS_PER_STARTUP: usize = 128;const fn default_enabled() -> bool { true}/// Preferred layout for the resume/fork session picker.#[derive(Serialize, Deserialize, Debug, Default, Copy, Clone, PartialEq, Eq, JsonSchema)]#[serde(rename_all = "kebab-case")]pub enum SessionPickerViewMode { Comfortable, #[default] Dense,}impl SessionPickerViewMode { pub const fn as_str(self) -> &'static str { match self { Self::Comfortable => "comfortable", Self::Dense => "dense", } }}impl fmt::Display for SessionPickerViewMode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.as_str()) }}/// Determine where Codex should store CLI auth credentials.#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]#[serde(rename_all = "lowercase")]pub enum AuthCredentialsStoreMode { #[default] /// Persist credentials in CODEX_HOME/auth.json. File, /// Persist credentials in the keyring. Fail if unavailable. Keyring, /// Use keyring when available; otherwise, fall back to a file in CODEX_HOME. Auto, /// Store credentials in memory only for the current process. Ephemeral,}/// Determine where Codex should store and read MCP credentials.#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]#[serde(rename_all = "lowercase")]pub enum OAuthCredentialsStoreMode { /// `Keyring` when available; otherwise, `File`. /// Credentials stored in the keyring will only be readable by Codex unless the user explicitly grants access via OS-level keyring access. #[default] Auto, /// CODEX_HOME/.credentials.json /// This file will be readable to Codex and other applications running as the same user. File, /// Keyring when available, otherwise fail. Keyring,}/// Determine how auth credentials should use keyring-backed storage.#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]#[serde(rename_all = "lowercase")]pub enum AuthKeyringBackendKind { /// Store the serialized auth payload directly in the OS keyring. Direct, /// Store auth payloads in the local encrypted secrets file, with the file key in the OS keyring. Secrets,}impl Default for AuthKeyringBackendKind { fn default() -> Self { if cfg!(windows) { Self::Secrets } else { Self::Direct } }}#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema)]#[serde(rename_all = "kebab-case")]pub enum WindowsSandboxModeToml { Elevated, Unelevated,}#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct WindowsToml { pub sandbox: Option<WindowsSandboxModeToml>, /// Defaults to `true`. Set to `false` to launch the final sandboxed child /// process on `Winsta0\\Default` instead of a private desktop. pub sandbox_private_desktop: Option<bool>,}#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, JsonSchema)]pub enum UriBasedFileOpener { #[serde(rename = "vscode")] VsCode, #[serde(rename = "vscode-insiders")] VsCodeInsiders, #[serde(rename = "windsurf")] Windsurf, #[serde(rename = "cursor")] Cursor, /// Option to disable the URI-based file opener. #[serde(rename = "none")] None,}impl UriBasedFileOpener { pub fn get_scheme(&self) -> Option<&str> { match self { UriBasedFileOpener::VsCode => Some("vscode"), UriBasedFileOpener::VsCodeInsiders => Some("vscode-insiders"), UriBasedFileOpener::Windsurf => Some("windsurf"), UriBasedFileOpener::Cursor => Some("cursor"), UriBasedFileOpener::None => None, } }}/// Settings that govern if and what will be written to `~/.codex/history.jsonl`.#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]#[serde(default)]#[schemars(deny_unknown_fields)]pub struct History { /// If true, history entries will not be written to disk. pub persistence: HistoryPersistence, /// If set, the maximum size of the history file in bytes. The oldest entries /// are dropped once the file exceeds this limit. pub max_bytes: Option<usize>,}#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Default, JsonSchema)]#[serde(rename_all = "kebab-case")]pub enum HistoryPersistence { /// Save all history entries to disk. #[default] SaveAll, /// Do not write history to disk. None,}// ===== Analytics configuration =====/// Analytics settings loaded from config.toml. Fields are optional so we can apply defaults.#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct AnalyticsConfigToml { /// When `false`, disables analytics across Codex product surfaces in this profile. pub enabled: Option<bool>,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct FeedbackConfigToml { /// When `false`, disables the feedback flow across Codex product surfaces. pub enabled: Option<bool>,}#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash, JsonSchema)]#[serde(rename_all = "snake_case")]pub enum ToolSuggestDiscoverableType { Connector, Plugin,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct ToolSuggestDiscoverable { #[serde(rename = "type")] pub kind: ToolSuggestDiscoverableType, pub id: String,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct ToolSuggestDisabledTool { #[serde(rename = "type")] pub kind: ToolSuggestDiscoverableType, pub id: String,}impl ToolSuggestDisabledTool { pub fn plugin(id: impl Into<String>) -> Self { Self { kind: ToolSuggestDiscoverableType::Plugin, id: id.into(), } } pub fn connector(id: impl Into<String>) -> Self { Self { kind: ToolSuggestDiscoverableType::Connector, id: id.into(), } } pub fn normalized(&self) -> Option<Self> { let id = self.id.trim(); (!id.is_empty()).then(|| Self { kind: self.kind, id: id.to_string(), }) }}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct ToolSuggestConfig { #[serde(default)] pub discoverables: Vec<ToolSuggestDiscoverable>, #[serde(default)] pub disabled_tools: Vec<ToolSuggestDisabledTool>,}/// Memories settings loaded from config.toml.#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct MemoriesToml { /// When `true`, external context sources mark the thread `memory_mode` as `"polluted"`. #[serde(alias = "no_memories_if_mcp_or_web_search")] pub disable_on_external_context: Option<bool>, /// When `false`, newly created threads are stored with `memory_mode = "disabled"` in the state DB. pub generate_memories: Option<bool>, /// When `false`, skip injecting memory usage instructions into developer prompts. pub use_memories: Option<bool>, /// When `true`, expose dedicated memory tools through the extension tool surface. pub dedicated_tools: Option<bool>, /// Maximum number of recent raw memories retained for global consolidation. #[schemars(range(min = 1, max = 4096))] pub max_raw_memories_for_consolidation: Option<usize>, /// Maximum number of days since a memory was last used before it becomes ineligible for phase 2 selection. pub max_unused_days: Option<i64>, /// Maximum age of the threads used for memories. pub max_rollout_age_days: Option<i64>, /// Maximum number of rollout candidates processed per pass. #[schemars(range(min = 1, max = 128))] pub max_rollouts_per_startup: Option<usize>, /// Minimum idle time between last thread activity and memory creation (hours). > 12h recommended. pub min_rollout_idle_hours: Option<i64>, /// Minimum remaining percentage required in Codex rate-limit windows before memory startup runs. #[schemars(range(min = 0, max = 100))] pub min_rate_limit_remaining_percent: Option<i64>, /// Model used for thread summarisation. pub extract_model: Option<String>, /// Model used for memory consolidation. pub consolidation_model: Option<String>,}/// Effective memories settings after defaults are applied.#[derive(Debug, Clone, PartialEq, Eq, Serialize)]pub struct MemoriesConfig { pub disable_on_external_context: bool, pub generate_memories: bool, pub use_memories: bool, pub dedicated_tools: bool, pub max_raw_memories_for_consolidation: usize, pub max_unused_days: i64, pub max_rollout_age_days: i64, pub max_rollouts_per_startup: usize, pub min_rollout_idle_hours: i64, pub min_rate_limit_remaining_percent: i64, pub extract_model: Option<String>, pub consolidation_model: Option<String>,}impl Default for MemoriesConfig { fn default() -> Self { Self { disable_on_external_context: false, generate_memories: true, use_memories: true, dedicated_tools: false, max_raw_memories_for_consolidation: DEFAULT_MEMORIES_MAX_RAW_MEMORIES_FOR_CONSOLIDATION, max_unused_days: DEFAULT_MEMORIES_MAX_UNUSED_DAYS, max_rollout_age_days: DEFAULT_MEMORIES_MAX_ROLLOUT_AGE_DAYS, max_rollouts_per_startup: DEFAULT_MEMORIES_MAX_ROLLOUTS_PER_STARTUP, min_rollout_idle_hours: DEFAULT_MEMORIES_MIN_ROLLOUT_IDLE_HOURS, min_rate_limit_remaining_percent: DEFAULT_MEMORIES_MIN_RATE_LIMIT_REMAINING_PERCENT, extract_model: None, consolidation_model: None, } }}impl From<MemoriesToml> for MemoriesConfig { fn from(toml: MemoriesToml) -> Self { let defaults = Self::default(); Self { disable_on_external_context: toml .disable_on_external_context .unwrap_or(defaults.disable_on_external_context), generate_memories: toml.generate_memories.unwrap_or(defaults.generate_memories), use_memories: toml.use_memories.unwrap_or(defaults.use_memories), dedicated_tools: toml.dedicated_tools.unwrap_or(defaults.dedicated_tools), max_raw_memories_for_consolidation: toml .max_raw_memories_for_consolidation .unwrap_or(defaults.max_raw_memories_for_consolidation) .clamp( MIN_MEMORIES_MAX_RAW_MEMORIES_FOR_CONSOLIDATION, MAX_MEMORIES_MAX_RAW_MEMORIES_FOR_CONSOLIDATION, ), max_unused_days: toml .max_unused_days .unwrap_or(defaults.max_unused_days) .clamp(0, 365), max_rollout_age_days: toml .max_rollout_age_days .unwrap_or(defaults.max_rollout_age_days) .clamp(0, 90), max_rollouts_per_startup: toml .max_rollouts_per_startup .unwrap_or(defaults.max_rollouts_per_startup) .clamp( MIN_MEMORIES_MAX_ROLLOUTS_PER_STARTUP, MAX_MEMORIES_MAX_ROLLOUTS_PER_STARTUP, ), min_rollout_idle_hours: toml .min_rollout_idle_hours .unwrap_or(defaults.min_rollout_idle_hours) .clamp(1, 48), min_rate_limit_remaining_percent: toml .min_rate_limit_remaining_percent .unwrap_or(defaults.min_rate_limit_remaining_percent) .clamp(0, 100), extract_model: toml.extract_model, consolidation_model: toml.consolidation_model, } }}/// Default settings that apply to all apps.#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct AppsDefaultConfig { /// When `false`, apps are disabled unless overridden by per-app settings. #[serde(default = "default_enabled")] pub enabled: bool, /// Reviewer for approval prompts unless overridden by per-app settings. #[serde(default, skip_serializing_if = "Option::is_none")] pub approvals_reviewer: Option<ApprovalsReviewer>, /// Whether tools with `destructive_hint = true` are allowed by default. #[serde( default = "default_enabled", skip_serializing_if = "std::clone::Clone::clone" )] pub destructive_enabled: bool, /// Whether tools with `open_world_hint = true` are allowed by default. #[serde( default = "default_enabled", skip_serializing_if = "std::clone::Clone::clone" )] pub open_world_enabled: bool,}/// Per-tool settings for a single app tool.#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct AppToolConfig { /// Whether this tool is enabled. `Some(true)` explicitly allows this tool. #[serde(default, skip_serializing_if = "Option::is_none")] pub enabled: Option<bool>, /// Approval mode for this tool. #[serde(default, skip_serializing_if = "Option::is_none")] pub approval_mode: Option<AppToolApproval>,}/// Tool settings for a single app.#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct AppToolsConfig { /// Per-tool overrides keyed by tool name (for example `repos/list`). #[serde(default, flatten)] pub tools: HashMap<String, AppToolConfig>,}/// Config values for a single app/connector.#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct AppConfig { /// When `false`, Codex does not surface this app. #[serde(default = "default_enabled")] pub enabled: bool, /// Reviewer for approval prompts from this app, overriding the thread default. #[serde(default, skip_serializing_if = "Option::is_none")] pub approvals_reviewer: Option<ApprovalsReviewer>, /// Whether tools with `destructive_hint = true` are allowed for this app. #[serde(default, skip_serializing_if = "Option::is_none")] pub destructive_enabled: Option<bool>, /// Whether tools with `open_world_hint = true` are allowed for this app. #[serde(default, skip_serializing_if = "Option::is_none")] pub open_world_enabled: Option<bool>, /// Approval mode for tools in this app unless a tool override exists. #[serde(default, skip_serializing_if = "Option::is_none")] pub default_tools_approval_mode: Option<AppToolApproval>, /// Whether tools are enabled by default for this app. #[serde(default, skip_serializing_if = "Option::is_none")] pub default_tools_enabled: Option<bool>, /// Per-tool settings for this app. #[serde(default, skip_serializing_if = "Option::is_none")] pub tools: Option<AppToolsConfig>,}/// App/connector settings loaded from `config.toml`.#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct AppsConfigToml { /// Default settings for all apps. #[serde(default, rename = "_default", skip_serializing_if = "Option::is_none")] pub default: Option<AppsDefaultConfig>, /// Per-app settings keyed by app ID (for example `[apps.google_drive]`). #[serde(default, flatten)] pub apps: HashMap<String, AppConfig>,}// ===== OTEL configuration =====#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema)]#[serde(rename_all = "kebab-case")]pub enum OtelHttpProtocol { /// Binary payload Binary, /// JSON payload Json,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]#[schemars(deny_unknown_fields)]#[serde(rename_all = "kebab-case")]pub struct OtelTlsConfig { pub ca_certificate: Option<AbsolutePathBuf>, pub client_certificate: Option<AbsolutePathBuf>, pub client_private_key: Option<AbsolutePathBuf>,}/// Which OTEL exporter to use.#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema)]#[schemars(deny_unknown_fields)]#[serde(rename_all = "kebab-case")]pub enum OtelExporterKind { None, Statsig, OtlpHttp { endpoint: String, #[serde(default)] headers: HashMap<String, String>, protocol: OtelHttpProtocol, #[serde(default)] tls: Option<OtelTlsConfig>, }, OtlpGrpc { endpoint: String, #[serde(default)] headers: HashMap<String, String>, #[serde(default)] tls: Option<OtelTlsConfig>, },}/// OTEL settings loaded from config.toml. Fields are optional so we can apply defaults.#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct OtelConfigToml { /// Log user prompt in traces pub log_user_prompt: Option<bool>, /// Mark traces with environment (dev, staging, prod, test). Defaults to dev. pub environment: Option<String>, /// Optional log exporter pub exporter: Option<OtelExporterKind>, /// Optional trace exporter pub trace_exporter: Option<OtelExporterKind>, /// Optional metrics exporter pub metrics_exporter: Option<OtelExporterKind>, /// Attributes to add to every exported trace span. pub span_attributes: Option<BTreeMap<String, String>>, /// Semicolon-separated `key:value` fields to upsert into W3C tracestate members. pub tracestate: Option<BTreeMap<String, BTreeMap<String, String>>>,}/// Effective OTEL settings after defaults are applied.#[derive(Debug, Clone, PartialEq)]pub struct OtelConfig { pub log_user_prompt: bool, pub environment: String, pub exporter: OtelExporterKind, pub trace_exporter: OtelExporterKind, pub metrics_exporter: OtelExporterKind, pub span_attributes: BTreeMap<String, String>, pub tracestate: BTreeMap<String, BTreeMap<String, String>>,}impl Default for OtelConfig { fn default() -> Self { OtelConfig { log_user_prompt: false, environment: DEFAULT_OTEL_ENVIRONMENT.to_owned(), exporter: OtelExporterKind::None, trace_exporter: OtelExporterKind::None, metrics_exporter: OtelExporterKind::Statsig, span_attributes: BTreeMap::new(), tracestate: BTreeMap::new(), } }}#[derive(Serialize, Debug, Clone, PartialEq, Eq, Deserialize, JsonSchema)]#[serde(untagged)]pub enum Notifications { Enabled(bool), Custom(Vec<String>),}impl Default for Notifications { fn default() -> Self { Self::Enabled(true) }}#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, Default)]#[serde(rename_all = "lowercase")]pub enum NotificationMethod { #[default] Auto, Osc9, Bel,}impl fmt::Display for NotificationMethod { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { NotificationMethod::Auto => write!(f, "auto"), NotificationMethod::Osc9 => write!(f, "osc9"), NotificationMethod::Bel => write!(f, "bel"), } }}#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, Default)]#[serde(rename_all = "lowercase")]pub enum NotificationCondition { /// Emit TUI notifications only while the terminal is unfocused. #[default] Unfocused, /// Emit TUI notifications regardless of terminal focus. Always,}impl fmt::Display for NotificationCondition { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { NotificationCondition::Unfocused => write!(f, "unfocused"), NotificationCondition::Always => write!(f, "always"), } }}#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, Default)]#[serde(rename_all = "kebab-case")]pub enum TuiPetAnchor { /// Anchor the pet to the bottom of the current TUI composer viewport. #[default] Composer, /// Anchor the pet to the physical bottom of the terminal screen. ScreenBottom,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct TuiNotificationSettings { /// Enable desktop notifications from the TUI. /// Defaults to `true`. #[serde(default, rename = "notifications")] pub notifications: Notifications, /// Notification method to use for terminal notifications. /// Defaults to `auto`. #[serde(default, rename = "notification_method")] pub method: NotificationMethod, /// Controls whether TUI notifications are delivered only when the terminal is unfocused or /// regardless of focus. Defaults to `unfocused`. #[serde(default, rename = "notification_condition")] pub condition: NotificationCondition,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct ModelAvailabilityNuxConfig { /// Number of times a startup availability NUX has been shown per model slug. #[serde(default, flatten)] pub shown_count: HashMap<String, u32>,}/// Fallback resize-reflow row cap when Codex cannot identify a terminal-specific scrollback size.pub const DEFAULT_TERMINAL_RESIZE_REFLOW_FALLBACK_MAX_ROWS: usize = 1_000;/// Collection of settings that are specific to the TUI.#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct Tui { #[serde(default, flatten)] pub notification_settings: TuiNotificationSettings, /// Enable animations (welcome screen, shimmer effects, spinners). /// Defaults to `true`. #[serde(default = "default_true")] pub animations: bool, /// Show startup tooltips in the TUI welcome screen. /// Defaults to `true`. #[serde(default = "default_true")] pub show_tooltips: bool, /// Start the composer in Vim mode (`Normal`) by default. /// Defaults to `false`. #[serde(default)] pub vim_mode_default: bool, /// Start the TUI in raw scrollback mode for copy-friendly transcript output. /// Defaults to `false`. #[serde(default)] pub raw_output_mode: bool, /// Controls whether the TUI uses the terminal's alternate screen buffer. /// /// - `auto` (default): Use alternate screen. /// - `always`: Always use alternate screen. /// - `never`: Never use alternate screen (inline mode only, preserves scrollback). #[serde(default)] pub alternate_screen: AltScreenMode, /// Ordered list of status line item identifiers. /// /// When set, the TUI renders the selected items as the status line. /// When unset, the TUI defaults to: `model-with-reasoning` and `current-dir`. #[serde(default)] pub status_line: Option<Vec<String>>, /// Color status line items with colors derived from the active syntax theme. /// Defaults to `true`. #[serde(default = "default_true")] pub status_line_use_colors: bool, /// Ordered list of terminal title item identifiers. /// /// When set, the TUI renders the selected items into the terminal window/tab title. /// When unset, the TUI defaults to: `activity` and `project`. /// The `activity` item spins while working and shows an action-required /// message when blocked on the user. #[serde(default)] pub terminal_title: Option<Vec<String>>, /// Syntax highlighting theme name (kebab-case). /// /// When set, overrides automatic light/dark theme detection. /// Use `/theme` in the TUI or see `$CODEX_HOME/themes` for custom themes. #[serde(default)] pub theme: Option<String>, /// Pet id to preselect in the terminal pet picker. /// /// Custom pet ids resolve against CODEX_HOME/pets/<pet-id>/pet.json. #[serde(default)] pub pet: Option<String>, /// Where the terminal pet should anchor vertically. /// /// Defaults to `composer`, which follows the current TUI composer viewport. #[serde(default)] pub pet_anchor: TuiPetAnchor, /// Preferred layout for resume/fork session picker results. #[serde(default)] pub session_picker_view: Option<SessionPickerViewMode>, /// Keybinding overrides for the TUI. /// /// This supports rebinding selected actions globally and by context. /// Context bindings take precedence over `global` bindings. #[serde(default)] pub keymap: TuiKeymap, /// Startup tooltip availability NUX state persisted by the TUI. #[serde(default)] pub model_availability_nux: ModelAvailabilityNuxConfig, /// Trim terminal resize-reflow replay to the most recent rendered terminal rows when the /// transcript exceeds this cap. Omit to use Codex's terminal-specific default. Set to `0` to /// keep all rendered rows. #[serde(default)] #[schemars(range(min = 0))] pub terminal_resize_reflow_max_rows: Option<usize>,}const fn default_true() -> bool { true}/// Settings for notices we display to users via the tui and app-server clients/// (primarily the Codex IDE extension). NOTE: these are different from/// notifications - notices are warnings, NUX screens, acknowledgements, etc.#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct ExternalConfigMigrationPrompts { /// Tracks whether home-level external config migration prompts are hidden. pub home: Option<bool>, /// Tracks the last time the home-level external config migration prompt was shown. pub home_last_prompted_at: Option<i64>, /// Tracks which project paths have opted out of external config migration prompts. #[serde(default)] pub projects: BTreeMap<String, bool>, /// Tracks the last time a project-level external config migration prompt was shown. #[serde(default)] pub project_last_prompted_at: BTreeMap<String, i64>,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct Notice { /// Tracks whether the user has acknowledged the full access warning prompt. pub hide_full_access_warning: Option<bool>, /// Tracks whether the user has acknowledged the Windows world-writable directories warning. pub hide_world_writable_warning: Option<bool>, /// Tracks whether the user opted out of Codex-managed fast defaults. pub fast_default_opt_out: Option<bool>, /// Tracks whether the user opted out of the rate limit model switch reminder. pub hide_rate_limit_model_nudge: Option<bool>, /// Tracks whether the user has seen the model migration prompt pub hide_gpt5_1_migration_prompt: Option<bool>, /// Tracks whether the user has seen the gpt-5.1-codex-max migration prompt #[serde(rename = "hide_gpt-5.1-codex-max_migration_prompt")] pub hide_gpt_5_1_codex_max_migration_prompt: Option<bool>, /// Tracks acknowledged model migrations as old->new model slug mappings. #[serde(default)] pub model_migrations: BTreeMap<String, String>, /// Tracks scopes where external config migration prompts should be suppressed. #[serde(default)] pub external_config_migration_prompts: ExternalConfigMigrationPrompts,}pub use crate::skills_config::BundledSkillsConfig;pub use crate::skills_config::SkillConfig;pub use crate::skills_config::SkillsConfig;#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct PluginConfig { #[serde(default = "default_enabled")] pub enabled: bool, /// Per-MCP-server policy overlays for MCP servers contributed by this plugin. #[serde(default, skip_serializing_if = "HashMap::is_empty")] pub mcp_servers: HashMap<String, PluginMcpServerConfig>,}/// Policy settings for a plugin-provided MCP server.////// This intentionally excludes transport settings: plugin manifests own how the/// MCP server is launched, while user config owns enablement and tool policy.#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct PluginMcpServerConfig { /// When `false`, Codex skips initializing this plugin MCP server. #[serde(default = "default_enabled")] pub enabled: bool, /// Approval mode for tools in this server unless a tool override exists. #[serde(default, skip_serializing_if = "Option::is_none")] pub default_tools_approval_mode: Option<AppToolApproval>, /// Explicit allow-list of tools exposed from this server. #[serde(default, skip_serializing_if = "Option::is_none")] pub enabled_tools: Option<Vec<String>>, /// Explicit deny-list of tools. These tools are removed after applying `enabled_tools`. #[serde(default, skip_serializing_if = "Option::is_none")] pub disabled_tools: Option<Vec<String>>, /// Per-tool approval settings keyed by tool name. #[serde(default, skip_serializing_if = "HashMap::is_empty")] pub tools: HashMap<String, McpServerToolConfig>,}impl Default for PluginMcpServerConfig { fn default() -> Self { Self { enabled: true, default_tools_approval_mode: None, enabled_tools: None, disabled_tools: None, tools: HashMap::new(), } }}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct MarketplaceConfig { /// Last time Codex successfully added or refreshed this marketplace. #[serde(default)] pub last_updated: Option<String>, /// Git revision Codex last successfully activated for this marketplace. #[serde(default)] pub last_revision: Option<String>, /// Source kind used to install this marketplace. #[serde(default)] pub source_type: Option<MarketplaceSourceType>, /// Source location used when the marketplace was added. #[serde(default)] pub source: Option<String>, /// Git ref to check out when `source_type` is `git`. #[serde(default, rename = "ref")] pub ref_name: Option<String>, /// Sparse checkout paths used when `source_type` is `git`. #[serde(default)] pub sparse_paths: Option<Vec<String>>,}#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema)]#[serde(rename_all = "snake_case")]pub enum MarketplaceSourceType { Git, Local,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct SandboxWorkspaceWrite { #[serde(default)] pub writable_roots: Vec<AbsolutePathBuf>, #[serde(default)] pub network_access: bool, #[serde(default)] pub exclude_tmpdir_env_var: bool, #[serde(default)] pub exclude_slash_tmp: bool,}impl From<SandboxWorkspaceWrite> for codex_app_server_protocol::SandboxSettings { fn from(sandbox_workspace_write: SandboxWorkspaceWrite) -> Self { Self { writable_roots: sandbox_workspace_write.writable_roots, network_access: Some(sandbox_workspace_write.network_access), exclude_tmpdir_env_var: Some(sandbox_workspace_write.exclude_tmpdir_env_var), exclude_slash_tmp: Some(sandbox_workspace_write.exclude_slash_tmp), } }}/// Policy for building the `env` when spawning a process via shell-like tools.#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct ShellEnvironmentPolicyToml { pub inherit: Option<ShellEnvironmentPolicyInherit>, pub ignore_default_excludes: Option<bool>, /// List of regular expressions. pub exclude: Option<Vec<String>>, pub r#set: Option<HashMap<String, String>>, /// List of regular expressions. pub include_only: Option<Vec<String>>, pub experimental_use_profile: Option<bool>,}impl From<ShellEnvironmentPolicyToml> for ShellEnvironmentPolicy { fn from(toml: ShellEnvironmentPolicyToml) -> Self { // Default to inheriting the full environment when not specified. let inherit = toml.inherit.unwrap_or(ShellEnvironmentPolicyInherit::All); let ignore_default_excludes = toml.ignore_default_excludes.unwrap_or(true); let exclude = toml .exclude .unwrap_or_default() .into_iter() .map(|s| EnvironmentVariablePattern::new_case_insensitive(&s)) .collect(); let r#set = toml.r#set.unwrap_or_default(); let include_only = toml .include_only .unwrap_or_default() .into_iter() .map(|s| EnvironmentVariablePattern::new_case_insensitive(&s)) .collect(); let use_profile = toml.experimental_use_profile.unwrap_or(false); Self { inherit, ignore_default_excludes, exclude, r#set, include_only, use_profile, } }}#[cfg(test)]#[path = "types_tests.rs"]mod tests;