From c285b889805743a9d77b7f9d1ff868f73eba89db Mon Sep 17 00:00:00 2001 From: sayan-oai Date: Wed, 21 Jan 2026 16:24:14 -0800 Subject: [PATCH] feat: publish config schema on release (#9572) Follow up to #8956; publish schema on new release to stable URL. Also canonicalize schema (sort keys) when writing. This avoids reliance on default `schema_rs` behavior and makes the schema easier to read. --- .github/workflows/rust-release.yml | 27 + codex-rs/core/config.schema.json | 1390 ++++++++++++++-------------- codex-rs/core/src/config/schema.rs | 43 +- 3 files changed, 746 insertions(+), 714 deletions(-) diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml index acc8b6baf..f058f8dbe 100644 --- a/.github/workflows/rust-release.yml +++ b/.github/workflows/rust-release.yml @@ -20,6 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@1.92 - name: Validate tag matches Cargo.toml version shell: bash @@ -45,6 +46,15 @@ jobs: echo "✅ Tag and Cargo.toml agree (${tag_ver})" echo "::endgroup::" + - name: Verify config schema fixture + shell: bash + working-directory: codex-rs + run: | + set -euo pipefail + echo "If this fails, run: just write-config-schema to overwrite fixture with intentional changes." + cargo run -p codex-core --bin codex-write-config-schema + git diff --exit-code core/config.schema.json + build: needs: tag-check name: Build - ${{ matrix.runner }} - ${{ matrix.target }} @@ -358,6 +368,10 @@ jobs: ls -R dist/ + - name: Add config schema release asset + run: | + cp codex-rs/core/config.schema.json dist/config-schema.json + - name: Define release name id: release_name run: | @@ -428,6 +442,19 @@ jobs: tag: ${{ github.ref_name }} config: .github/dotslash-config.json + - name: Trigger developers.openai.com deploy + # Only trigger the deploy if the release is not a pre-release. + # The deploy is used to update the developers.openai.com website with the new config schema json file. + if: ${{ !contains(steps.release_name.outputs.name, '-') }} + continue-on-error: true + env: + DEV_WEBSITE_VERCEL_DEPLOY_HOOK_URL: ${{ secrets.DEV_WEBSITE_VERCEL_DEPLOY_HOOK_URL }} + run: | + if ! curl -sS -f -o /dev/null -X POST "$DEV_WEBSITE_VERCEL_DEPLOY_HOOK_URL"; then + echo "::warning title=developers.openai.com deploy hook failed::Vercel deploy hook POST failed for ${GITHUB_REF_NAME}" + exit 1 + fi + # Publish to npm using OIDC authentication. # July 31, 2025: https://github.blog/changelog/2025-07-31-npm-trusted-publishing-with-oidc-is-generally-available/ # npm docs: https://docs.npmjs.com/trusted-publishers diff --git a/codex-rs/core/config.schema.json b/codex-rs/core/config.schema.json index e9ec0d7e6..92e36bd79 100644 --- a/codex-rs/core/config.schema.json +++ b/codex-rs/core/config.schema.json @@ -1,443 +1,5 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ConfigToml", - "description": "Base config deserialized from ~/.codex/config.toml.", - "type": "object", - "properties": { - "agents": { - "description": "Agent-related settings (thread limits, etc.).", - "allOf": [ - { - "$ref": "#/definitions/AgentsToml" - } - ] - }, - "analytics": { - "description": "When `false`, disables analytics across Codex product surfaces in this machine. Defaults to `true`.", - "allOf": [ - { - "$ref": "#/definitions/AnalyticsConfigToml" - } - ] - }, - "approval_policy": { - "description": "Default approval policy for executing commands.", - "allOf": [ - { - "$ref": "#/definitions/AskForApproval" - } - ] - }, - "chatgpt_base_url": { - "description": "Base URL for requests to ChatGPT (as opposed to the OpenAI API).", - "type": "string" - }, - "check_for_update_on_startup": { - "description": "When `true`, checks for Codex updates on startup and surfaces update prompts. Set to `false` only if your Codex updates are centrally managed. Defaults to `true`.", - "type": "boolean" - }, - "cli_auth_credentials_store": { - "description": "Preferred backend for storing CLI auth credentials. file (default): Use a file in the Codex home directory. keyring: Use an OS-specific keyring service. auto: Use the keyring if available, otherwise use a file.", - "default": null, - "allOf": [ - { - "$ref": "#/definitions/AuthCredentialsStoreMode" - } - ] - }, - "compact_prompt": { - "description": "Compact prompt used for history compaction.", - "type": "string" - }, - "developer_instructions": { - "description": "Developer instructions inserted as a `developer` role message.", - "default": null, - "type": "string" - }, - "disable_paste_burst": { - "description": "When true, disables burst-paste detection for typed input entirely. All characters are inserted as they are received, and no buffering or placeholder replacement will occur for fast keypress bursts.", - "type": "boolean" - }, - "experimental_compact_prompt_file": { - "$ref": "#/definitions/AbsolutePathBuf" - }, - "experimental_use_freeform_apply_patch": { - "type": "boolean" - }, - "experimental_use_unified_exec_tool": { - "type": "boolean" - }, - "features": { - "description": "Centralized feature flags (new). Prefer this over individual toggles.", - "default": null, - "type": "object", - "properties": { - "apply_patch_freeform": { - "type": "boolean" - }, - "child_agents_md": { - "type": "boolean" - }, - "collab": { - "type": "boolean" - }, - "collaboration_modes": { - "type": "boolean" - }, - "elevated_windows_sandbox": { - "type": "boolean" - }, - "enable_experimental_windows_sandbox": { - "type": "boolean" - }, - "enable_request_compression": { - "type": "boolean" - }, - "exec_policy": { - "type": "boolean" - }, - "experimental_use_freeform_apply_patch": { - "type": "boolean" - }, - "experimental_use_unified_exec_tool": { - "type": "boolean" - }, - "experimental_windows_sandbox": { - "type": "boolean" - }, - "include_apply_patch_tool": { - "type": "boolean" - }, - "powershell_utf8": { - "type": "boolean" - }, - "remote_compaction": { - "type": "boolean" - }, - "remote_models": { - "type": "boolean" - }, - "responses_websockets": { - "type": "boolean" - }, - "shell_snapshot": { - "type": "boolean" - }, - "shell_tool": { - "type": "boolean" - }, - "steer": { - "type": "boolean" - }, - "tui2": { - "type": "boolean" - }, - "undo": { - "type": "boolean" - }, - "unified_exec": { - "type": "boolean" - }, - "web_search": { - "type": "boolean" - }, - "web_search_cached": { - "type": "boolean" - }, - "web_search_request": { - "type": "boolean" - } - }, - "additionalProperties": false - }, - "feedback": { - "description": "When `false`, disables feedback collection across Codex product surfaces. Defaults to `true`.", - "allOf": [ - { - "$ref": "#/definitions/FeedbackConfigToml" - } - ] - }, - "file_opener": { - "description": "Optional URI-based file opener. If set, citations to files in the model output will be hyperlinked using the specified URI scheme.", - "allOf": [ - { - "$ref": "#/definitions/UriBasedFileOpener" - } - ] - }, - "forced_chatgpt_workspace_id": { - "description": "When set, restricts ChatGPT login to a specific workspace identifier.", - "default": null, - "type": "string" - }, - "forced_login_method": { - "description": "When set, restricts the login mechanism users may use.", - "default": null, - "allOf": [ - { - "$ref": "#/definitions/ForcedLoginMethod" - } - ] - }, - "ghost_snapshot": { - "description": "Settings for ghost snapshots (used for undo).", - "default": null, - "allOf": [ - { - "$ref": "#/definitions/GhostSnapshotToml" - } - ] - }, - "hide_agent_reasoning": { - "description": "When set to `true`, `AgentReasoning` events will be hidden from the UI/output. Defaults to `false`.", - "type": "boolean" - }, - "history": { - "description": "Settings that govern if and what will be written to `~/.codex/history.jsonl`.", - "default": null, - "allOf": [ - { - "$ref": "#/definitions/History" - } - ] - }, - "instructions": { - "description": "System instructions.", - "type": "string" - }, - "mcp_oauth_callback_port": { - "description": "Optional fixed port for the local HTTP callback server used during MCP OAuth login. When unset, Codex will bind to an ephemeral port chosen by the OS.", - "type": "integer", - "format": "uint16", - "minimum": 0.0 - }, - "mcp_oauth_credentials_store": { - "description": "Preferred backend for storing MCP OAuth credentials. keyring: Use an OS-specific keyring service. https://github.com/openai/codex/blob/main/codex-rs/rmcp-client/src/oauth.rs#L2 file: Use a file in the Codex home directory. auto (default): Use the OS-specific keyring service if available, otherwise use a file.", - "default": null, - "allOf": [ - { - "$ref": "#/definitions/OAuthCredentialsStoreMode" - } - ] - }, - "mcp_servers": { - "description": "Definition for MCP servers that Codex can reach out to for tool calls.", - "default": {}, - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/RawMcpServerConfig" - } - }, - "model": { - "description": "Optional override of model selection.", - "type": "string" - }, - "model_auto_compact_token_limit": { - "description": "Token usage threshold triggering auto-compaction of conversation history.", - "type": "integer", - "format": "int64" - }, - "model_context_window": { - "description": "Size of the context window for the model, in tokens.", - "type": "integer", - "format": "int64" - }, - "model_instructions_file": { - "description": "Optional path to a file containing model instructions that will override the built-in instructions for the selected model. Users are STRONGLY DISCOURAGED from using this field, as deviating from the instructions sanctioned by Codex will likely degrade model performance.", - "allOf": [ - { - "$ref": "#/definitions/AbsolutePathBuf" - } - ] - }, - "model_personality": { - "description": "EXPERIMENTAL Optionally specify a personality for the model", - "allOf": [ - { - "$ref": "#/definitions/Personality" - } - ] - }, - "model_provider": { - "description": "Provider to use from the model_providers map.", - "type": "string" - }, - "model_providers": { - "description": "User-defined provider entries that extend/override the built-in list.", - "default": {}, - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ModelProviderInfo" - } - }, - "model_reasoning_effort": { - "$ref": "#/definitions/ReasoningEffort" - }, - "model_reasoning_summary": { - "$ref": "#/definitions/ReasoningSummary" - }, - "model_supports_reasoning_summaries": { - "description": "Override to force-enable reasoning summaries for the configured model.", - "type": "boolean" - }, - "model_verbosity": { - "description": "Optional verbosity control for GPT-5 models (Responses API `text.verbosity`).", - "allOf": [ - { - "$ref": "#/definitions/Verbosity" - } - ] - }, - "notice": { - "description": "Collection of in-product notices (different from notifications) See [`crate::config::types::Notices`] for more details", - "allOf": [ - { - "$ref": "#/definitions/Notice" - } - ] - }, - "notify": { - "description": "Optional external command to spawn for end-user notifications.", - "default": null, - "type": "array", - "items": { - "type": "string" - } - }, - "oss_provider": { - "description": "Preferred OSS provider for local models, e.g. \"lmstudio\", \"ollama\", or \"ollama-chat\".", - "type": "string" - }, - "otel": { - "description": "OTEL configuration.", - "allOf": [ - { - "$ref": "#/definitions/OtelConfigToml" - } - ] - }, - "profile": { - "description": "Profile to use from the `profiles` map.", - "type": "string" - }, - "profiles": { - "description": "Named profiles to facilitate switching between different configurations.", - "default": {}, - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ConfigProfile" - } - }, - "project_doc_fallback_filenames": { - "description": "Ordered list of fallback filenames to look for when AGENTS.md is missing.", - "type": "array", - "items": { - "type": "string" - } - }, - "project_doc_max_bytes": { - "description": "Maximum number of bytes to include from an AGENTS.md project doc file.", - "type": "integer", - "format": "uint", - "minimum": 0.0 - }, - "project_root_markers": { - "description": "Markers used to detect the project root when searching parent directories for `.codex` folders. Defaults to [\".git\"] when unset.", - "default": null, - "type": "array", - "items": { - "type": "string" - } - }, - "projects": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ProjectConfig" - } - }, - "review_model": { - "description": "Review model override used by the `/review` feature.", - "type": "string" - }, - "sandbox_mode": { - "description": "Sandbox mode to use.", - "allOf": [ - { - "$ref": "#/definitions/SandboxMode" - } - ] - }, - "sandbox_workspace_write": { - "description": "Sandbox configuration to apply if `sandbox` is `WorkspaceWrite`.", - "allOf": [ - { - "$ref": "#/definitions/SandboxWorkspaceWrite" - } - ] - }, - "shell_environment_policy": { - "default": { - "exclude": null, - "experimental_use_profile": null, - "ignore_default_excludes": null, - "include_only": null, - "inherit": null, - "set": null - }, - "allOf": [ - { - "$ref": "#/definitions/ShellEnvironmentPolicyToml" - } - ] - }, - "show_raw_agent_reasoning": { - "description": "When set to `true`, `AgentReasoningRawContentEvent` events will be shown in the UI/output. Defaults to `false`.", - "type": "boolean" - }, - "skills": { - "description": "User-level skill config entries keyed by SKILL.md path.", - "allOf": [ - { - "$ref": "#/definitions/SkillsConfig" - } - ] - }, - "tool_output_token_limit": { - "description": "Token budget applied when storing tool/function outputs in the context manager.", - "type": "integer", - "format": "uint", - "minimum": 0.0 - }, - "tools": { - "description": "Nested tools section for feature toggles", - "allOf": [ - { - "$ref": "#/definitions/ToolsToml" - } - ] - }, - "tui": { - "description": "Collection of settings that are specific to the TUI.", - "allOf": [ - { - "$ref": "#/definitions/Tui" - } - ] - }, - "web_search": { - "description": "Controls the web search tool mode: disabled, cached, or live.", - "allOf": [ - { - "$ref": "#/definitions/WebSearchMode" - } - ] - }, - "windows_wsl_setup_acknowledged": { - "description": "Tracks whether the Windows onboarding screen has been acknowledged.", - "type": "boolean" - } - }, "additionalProperties": false, "definitions": { "AbsolutePathBuf": { @@ -445,84 +7,84 @@ "type": "string" }, "AgentsToml": { - "type": "object", + "additionalProperties": false, "properties": { "max_threads": { "description": "Maximum number of agent threads that can be open concurrently. When unset, no limit is enforced.", - "type": "integer", "format": "uint", - "minimum": 1.0 + "minimum": 1.0, + "type": "integer" } }, - "additionalProperties": false + "type": "object" }, "AltScreenMode": { "description": "Controls whether the TUI uses the terminal's alternate screen buffer.\n\n**Background:** The alternate screen buffer provides a cleaner fullscreen experience without polluting the terminal's scrollback history. However, it conflicts with terminal multiplexers like Zellij that strictly follow the xterm specification, which defines that alternate screen buffers should not have scrollback.\n\n**Zellij's behavior:** Zellij intentionally disables scrollback in alternate screen mode (see https://github.com/zellij-org/zellij/pull/1032) to comply with the xterm spec. This is by design and not configurable in Zellij—there is no option to enable scrollback in alternate screen mode.\n\n**Solution:** This setting provides a pragmatic workaround: - `auto` (default): Automatically detect the terminal multiplexer. If running in Zellij, disable alternate screen to preserve scrollback. Enable it everywhere else. - `always`: Always use alternate screen mode (original behavior before this fix). - `never`: Never use alternate screen mode. Runs in inline mode, preserving scrollback in all multiplexers.\n\nThe CLI flag `--no-alt-screen` can override this setting at runtime.", "oneOf": [ { "description": "Auto-detect: disable alternate screen in Zellij, enable elsewhere.", - "type": "string", "enum": [ "auto" - ] + ], + "type": "string" }, { "description": "Always use alternate screen (original behavior).", - "type": "string", "enum": [ "always" - ] + ], + "type": "string" }, { "description": "Never use alternate screen (inline mode only).", - "type": "string", "enum": [ "never" - ] + ], + "type": "string" } ] }, "AnalyticsConfigToml": { + "additionalProperties": false, "description": "Analytics settings loaded from config.toml. Fields are optional so we can apply defaults.", - "type": "object", "properties": { "enabled": { "description": "When `false`, disables analytics across Codex product surfaces in this profile.", "type": "boolean" } }, - "additionalProperties": false + "type": "object" }, "AskForApproval": { "description": "Determines the conditions under which the user is consulted to approve running the command proposed by Codex.", "oneOf": [ { "description": "Under this policy, only \"known safe\" commands—as determined by `is_safe_command()`—that **only read files** are auto‑approved. Everything else will ask the user to approve.", - "type": "string", "enum": [ "untrusted" - ] + ], + "type": "string" }, { "description": "*All* commands are auto‑approved, but they are expected to run inside a sandbox where network access is disabled and writes are confined to a specific set of paths. If the command fails, it will be escalated to the user to approve execution without a sandbox.", - "type": "string", "enum": [ "on-failure" - ] + ], + "type": "string" }, { "description": "The model decides when to ask the user for approval.", - "type": "string", "enum": [ "on-request" - ] + ], + "type": "string" }, { "description": "Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.", - "type": "string", "enum": [ "never" - ] + ], + "type": "string" } ] }, @@ -531,30 +93,30 @@ "oneOf": [ { "description": "Persist credentials in CODEX_HOME/auth.json.", - "type": "string", "enum": [ "file" - ] + ], + "type": "string" }, { "description": "Persist credentials in the keyring. Fail if unavailable.", - "type": "string", "enum": [ "keyring" - ] + ], + "type": "string" }, { "description": "Use keyring when available; otherwise, fall back to a file in CODEX_HOME.", - "type": "string", "enum": [ "auto" - ] + ], + "type": "string" } ] }, "ConfigProfile": { + "additionalProperties": false, "description": "Collection of common configuration options that a user can define as a unit in `config.toml`.", - "type": "object", "properties": { "analytics": { "$ref": "#/definitions/AnalyticsConfigToml" @@ -575,9 +137,9 @@ "type": "boolean" }, "features": { - "description": "Optional feature toggles scoped to this profile.", + "additionalProperties": false, "default": null, - "type": "object", + "description": "Optional feature toggles scoped to this profile.", "properties": { "apply_patch_freeform": { "type": "boolean" @@ -655,7 +217,7 @@ "type": "boolean" } }, - "additionalProperties": false + "type": "object" }, "include_apply_patch_tool": { "type": "boolean" @@ -664,12 +226,12 @@ "type": "string" }, "model_instructions_file": { - "description": "Optional path to a file containing model instructions.", "allOf": [ { "$ref": "#/definitions/AbsolutePathBuf" } - ] + ], + "description": "Optional path to a file containing model instructions." }, "model_personality": { "$ref": "#/definitions/Personality" @@ -703,27 +265,27 @@ "$ref": "#/definitions/WebSearchMode" } }, - "additionalProperties": false + "type": "object" }, "FeedbackConfigToml": { - "type": "object", + "additionalProperties": false, "properties": { "enabled": { "description": "When `false`, disables the feedback flow across Codex product surfaces.", "type": "boolean" } }, - "additionalProperties": false + "type": "object" }, "ForcedLoginMethod": { - "type": "string", "enum": [ "chatgpt", "api" - ] + ], + "type": "string" }, "GhostSnapshotToml": { - "type": "object", + "additionalProperties": false, "properties": { "disable_warnings": { "description": "Disable all ghost snapshot warning events.", @@ -731,76 +293,73 @@ }, "ignore_large_untracked_dirs": { "description": "Ignore untracked directories that contain this many files or more. (Still emits a warning unless warnings are disabled.)", - "type": "integer", - "format": "int64" + "format": "int64", + "type": "integer" }, "ignore_large_untracked_files": { "description": "Exclude untracked files larger than this many bytes from ghost snapshots.", - "type": "integer", - "format": "int64" + "format": "int64", + "type": "integer" } }, - "additionalProperties": false + "type": "object" }, "History": { + "additionalProperties": false, "description": "Settings that govern if and what will be written to `~/.codex/history.jsonl`.", - "type": "object", - "required": [ - "persistence" - ], "properties": { "max_bytes": { "description": "If set, the maximum size of the history file in bytes. The oldest entries are dropped once the file exceeds this limit.", - "type": "integer", "format": "uint", - "minimum": 0.0 + "minimum": 0.0, + "type": "integer" }, "persistence": { - "description": "If true, history entries will not be written to disk.", "allOf": [ { "$ref": "#/definitions/HistoryPersistence" } - ] + ], + "description": "If true, history entries will not be written to disk." } }, - "additionalProperties": false + "required": [ + "persistence" + ], + "type": "object" }, "HistoryPersistence": { "oneOf": [ { "description": "Save all history entries to disk.", - "type": "string", "enum": [ "save-all" - ] + ], + "type": "string" }, { "description": "Do not write history to disk.", - "type": "string", "enum": [ "none" - ] + ], + "type": "string" } ] }, "ModelProviderInfo": { + "additionalProperties": false, "description": "Serializable representation of a provider definition.", - "type": "object", - "required": [ - "name" - ], "properties": { "base_url": { "description": "Base URL for the provider's OpenAI-compatible API.", "type": "string" }, "env_http_headers": { - "description": "Optional HTTP headers to include in requests to this provider where the (key, value) pairs are the header name and _environment variable_ whose value should be used. If the environment variable is not set, or the value is empty, the header will not be included in the request.", - "type": "object", "additionalProperties": { "type": "string" - } + }, + "description": "Optional HTTP headers to include in requests to this provider where the (key, value) pairs are the header name and _environment variable_ whose value should be used. If the environment variable is not set, or the value is empty, the header will not be included in the request.", + "type": "object" }, "env_key": { "description": "Environment variable that stores the user's API key for this provider.", @@ -815,61 +374,64 @@ "type": "string" }, "http_headers": { - "description": "Additional HTTP headers to include in requests to this provider where the (key, value) pairs are the header name and value.", - "type": "object", "additionalProperties": { "type": "string" - } + }, + "description": "Additional HTTP headers to include in requests to this provider where the (key, value) pairs are the header name and value.", + "type": "object" }, "name": { "description": "Friendly display name.", "type": "string" }, "query_params": { - "description": "Optional query parameters to append to the base URL.", - "type": "object", "additionalProperties": { "type": "string" - } + }, + "description": "Optional query parameters to append to the base URL.", + "type": "object" }, "request_max_retries": { "description": "Maximum number of times to retry a failed HTTP request to this provider.", - "type": "integer", "format": "uint64", - "minimum": 0.0 + "minimum": 0.0, + "type": "integer" }, "requires_openai_auth": { - "description": "Does this provider require an OpenAI API Key or ChatGPT login token? If true, user is presented with login screen on first run, and login preference and token/key are stored in auth.json. If false (which is the default), login screen is skipped, and API key (if needed) comes from the \"env_key\" environment variable.", "default": false, + "description": "Does this provider require an OpenAI API Key or ChatGPT login token? If true, user is presented with login screen on first run, and login preference and token/key are stored in auth.json. If false (which is the default), login screen is skipped, and API key (if needed) comes from the \"env_key\" environment variable.", "type": "boolean" }, "stream_idle_timeout_ms": { "description": "Idle timeout (in milliseconds) to wait for activity on a streaming response before treating the connection as lost.", - "type": "integer", "format": "uint64", - "minimum": 0.0 + "minimum": 0.0, + "type": "integer" }, "stream_max_retries": { "description": "Number of times to retry reconnecting a dropped streaming response before failing.", - "type": "integer", "format": "uint64", - "minimum": 0.0 + "minimum": 0.0, + "type": "integer" }, "wire_api": { - "description": "Which wire protocol this provider expects.", - "default": "chat", "allOf": [ { "$ref": "#/definitions/WireApi" } - ] + ], + "default": "chat", + "description": "Which wire protocol this provider expects." } }, - "additionalProperties": false + "required": [ + "name" + ], + "type": "object" }, "Notice": { + "additionalProperties": false, "description": "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.", - "type": "object", "properties": { "hide_full_access_warning": { "description": "Tracks whether the user has acknowledged the full access warning prompt.", @@ -892,15 +454,15 @@ "type": "boolean" }, "model_migrations": { - "description": "Tracks acknowledged model migrations as old->new model slug mappings.", - "default": {}, - "type": "object", "additionalProperties": { "type": "string" - } + }, + "default": {}, + "description": "Tracks acknowledged model migrations as old->new model slug mappings.", + "type": "object" } }, - "additionalProperties": false + "type": "object" }, "Notifications": { "anyOf": [ @@ -908,10 +470,10 @@ "type": "boolean" }, { - "type": "array", "items": { "type": "string" - } + }, + "type": "array" } ] }, @@ -920,143 +482,143 @@ "oneOf": [ { "description": "`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.", - "type": "string", "enum": [ "auto" - ] + ], + "type": "string" }, { "description": "CODEX_HOME/.credentials.json This file will be readable to Codex and other applications running as the same user.", - "type": "string", "enum": [ "file" - ] + ], + "type": "string" }, { "description": "Keyring when available, otherwise fail.", - "type": "string", "enum": [ "keyring" - ] + ], + "type": "string" } ] }, "OtelConfigToml": { + "additionalProperties": false, "description": "OTEL settings loaded from config.toml. Fields are optional so we can apply defaults.", - "type": "object", "properties": { "environment": { "description": "Mark traces with environment (dev, staging, prod, test). Defaults to dev.", "type": "string" }, "exporter": { - "description": "Optional log exporter", "allOf": [ { "$ref": "#/definitions/OtelExporterKind" } - ] + ], + "description": "Optional log exporter" }, "log_user_prompt": { "description": "Log user prompt in traces", "type": "boolean" }, "trace_exporter": { - "description": "Optional trace exporter", "allOf": [ { "$ref": "#/definitions/OtelExporterKind" } - ] + ], + "description": "Optional trace exporter" } }, - "additionalProperties": false + "type": "object" }, "OtelExporterKind": { "description": "Which OTEL exporter to use.", "oneOf": [ { - "type": "string", "enum": [ "none", "statsig" - ] + ], + "type": "string" }, { - "type": "object", - "required": [ - "otlp-http" - ], + "additionalProperties": false, "properties": { "otlp-http": { - "type": "object", - "required": [ - "endpoint", - "protocol" - ], + "additionalProperties": false, "properties": { "endpoint": { "type": "string" }, "headers": { - "default": {}, - "type": "object", "additionalProperties": { "type": "string" - } + }, + "default": {}, + "type": "object" }, "protocol": { "$ref": "#/definitions/OtelHttpProtocol" }, "tls": { - "default": null, "allOf": [ { "$ref": "#/definitions/OtelTlsConfig" } - ] + ], + "default": null } }, - "additionalProperties": false + "required": [ + "endpoint", + "protocol" + ], + "type": "object" } }, - "additionalProperties": false + "required": [ + "otlp-http" + ], + "type": "object" }, { - "type": "object", - "required": [ - "otlp-grpc" - ], + "additionalProperties": false, "properties": { "otlp-grpc": { - "type": "object", - "required": [ - "endpoint" - ], + "additionalProperties": false, "properties": { "endpoint": { "type": "string" }, "headers": { - "default": {}, - "type": "object", "additionalProperties": { "type": "string" - } + }, + "default": {}, + "type": "object" }, "tls": { - "default": null, "allOf": [ { "$ref": "#/definitions/OtelTlsConfig" } - ] + ], + "default": null } }, - "additionalProperties": false + "required": [ + "endpoint" + ], + "type": "object" } }, - "additionalProperties": false + "required": [ + "otlp-grpc" + ], + "type": "object" } ] }, @@ -1064,22 +626,22 @@ "oneOf": [ { "description": "Binary payload", - "type": "string", "enum": [ "binary" - ] + ], + "type": "string" }, { "description": "JSON payload", - "type": "string", "enum": [ "json" - ] + ], + "type": "string" } ] }, "OtelTlsConfig": { - "type": "object", + "additionalProperties": false, "properties": { "ca-certificate": { "$ref": "#/definitions/AbsolutePathBuf" @@ -1091,33 +653,33 @@ "$ref": "#/definitions/AbsolutePathBuf" } }, - "additionalProperties": false + "type": "object" }, "Personality": { - "type": "string", "enum": [ "friendly", "pragmatic" - ] + ], + "type": "string" }, "ProjectConfig": { - "type": "object", + "additionalProperties": false, "properties": { "trust_level": { "$ref": "#/definitions/TrustLevel" } }, - "additionalProperties": false + "type": "object" }, "RawMcpServerConfig": { - "type": "object", + "additionalProperties": false, "properties": { "args": { "default": null, - "type": "array", "items": { "type": "string" - } + }, + "type": "array" }, "bearer_token": { "type": "string" @@ -1134,10 +696,10 @@ }, "disabled_tools": { "default": null, - "type": "array", "items": { "type": "string" - } + }, + "type": "array" }, "enabled": { "default": null, @@ -1145,63 +707,62 @@ }, "enabled_tools": { "default": null, - "type": "array", "items": { "type": "string" - } + }, + "type": "array" }, "env": { - "default": null, - "type": "object", "additionalProperties": { "type": "string" - } + }, + "default": null, + "type": "object" }, "env_http_headers": { - "default": null, - "type": "object", "additionalProperties": { "type": "string" - } + }, + "default": null, + "type": "object" }, "env_vars": { "default": null, - "type": "array", "items": { "type": "string" - } + }, + "type": "array" }, "http_headers": { - "type": "object", "additionalProperties": { "type": "string" - } + }, + "type": "object" }, "startup_timeout_ms": { "default": null, - "type": "integer", "format": "uint64", - "minimum": 0.0 + "minimum": 0.0, + "type": "integer" }, "startup_timeout_sec": { "default": null, - "type": "number", - "format": "double" + "format": "double", + "type": "number" }, "tool_timeout_sec": { "default": null, - "type": "number", - "format": "double" + "format": "double", + "type": "number" }, "url": { "type": "string" } }, - "additionalProperties": false + "type": "object" }, "ReasoningEffort": { "description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning", - "type": "string", "enum": [ "none", "minimal", @@ -1209,38 +770,39 @@ "medium", "high", "xhigh" - ] + ], + "type": "string" }, "ReasoningSummary": { "description": "A summary of the reasoning performed by the model. This can be useful for debugging and understanding the model's reasoning process. See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#reasoning-summaries", "oneOf": [ { - "type": "string", "enum": [ "auto", "concise", "detailed" - ] + ], + "type": "string" }, { "description": "Option to disable reasoning summaries.", - "type": "string", "enum": [ "none" - ] + ], + "type": "string" } ] }, "SandboxMode": { - "type": "string", "enum": [ "read-only", "workspace-write", "danger-full-access" - ] + ], + "type": "string" }, "SandboxWorkspaceWrite": { - "type": "object", + "additionalProperties": false, "properties": { "exclude_slash_tmp": { "default": false, @@ -1256,37 +818,37 @@ }, "writable_roots": { "default": [], - "type": "array", "items": { "$ref": "#/definitions/AbsolutePathBuf" - } + }, + "type": "array" } }, - "additionalProperties": false + "type": "object" }, "ScrollInputMode": { "description": "How TUI2 should interpret mouse scroll events.\n\nTerminals generally encode both mouse wheels and trackpads as the same \"scroll up/down\" mouse button events, without a magnitude. This setting controls whether Codex uses a heuristic to infer wheel vs trackpad per stream, or forces a specific behavior.", "oneOf": [ { "description": "Infer wheel vs trackpad behavior per scroll stream.", - "type": "string", "enum": [ "auto" - ] + ], + "type": "string" }, { "description": "Always treat scroll events as mouse-wheel input (fixed lines per tick).", - "type": "string", "enum": [ "wheel" - ] + ], + "type": "string" }, { "description": "Always treat scroll events as trackpad input (fractional accumulation).", - "type": "string", "enum": [ "trackpad" - ] + ], + "type": "string" } ] }, @@ -1294,37 +856,37 @@ "oneOf": [ { "description": "\"Core\" environment variables for the platform. On UNIX, this would include HOME, LOGNAME, PATH, SHELL, and USER, among others.", - "type": "string", "enum": [ "core" - ] + ], + "type": "string" }, { "description": "Inherits the full environment from the parent process.", - "type": "string", "enum": [ "all" - ] + ], + "type": "string" }, { "description": "Do not inherit any environment variables from the parent process.", - "type": "string", "enum": [ "none" - ] + ], + "type": "string" } ] }, "ShellEnvironmentPolicyToml": { + "additionalProperties": false, "description": "Policy for building the `env` when spawning a process via either the `shell` or `local_shell` tool.", - "type": "object", "properties": { "exclude": { "description": "List of regular expressions.", - "type": "array", "items": { "type": "string" - } + }, + "type": "array" }, "experimental_use_profile": { "type": "boolean" @@ -1334,29 +896,25 @@ }, "include_only": { "description": "List of regular expressions.", - "type": "array", "items": { "type": "string" - } + }, + "type": "array" }, "inherit": { "$ref": "#/definitions/ShellEnvironmentPolicyInherit" }, "set": { - "type": "object", "additionalProperties": { "type": "string" - } + }, + "type": "object" } }, - "additionalProperties": false + "type": "object" }, "SkillConfig": { - "type": "object", - "required": [ - "enabled", - "path" - ], + "additionalProperties": false, "properties": { "enabled": { "type": "boolean" @@ -1365,26 +923,30 @@ "$ref": "#/definitions/AbsolutePathBuf" } }, - "additionalProperties": false + "required": [ + "enabled", + "path" + ], + "type": "object" }, "SkillsConfig": { - "type": "object", + "additionalProperties": false, "properties": { "config": { - "type": "array", "items": { "$ref": "#/definitions/SkillConfig" - } + }, + "type": "array" } }, - "additionalProperties": false + "type": "object" }, "ToolsToml": { - "type": "object", + "additionalProperties": false, "properties": { "view_image": { - "description": "Enable the `view_image` tool that lets the agent attach local images.", "default": null, + "description": "Enable the `view_image` tool that lets the agent attach local images.", "type": "boolean" }, "web_search": { @@ -1392,169 +954,607 @@ "type": "boolean" } }, - "additionalProperties": false + "type": "object" }, "TrustLevel": { "description": "Represents the trust level for a project directory. This determines the approval policy and sandbox mode applied.", - "type": "string", "enum": [ "trusted", "untrusted" - ] + ], + "type": "string" }, "Tui": { + "additionalProperties": false, "description": "Collection of settings that are specific to the TUI.", - "type": "object", "properties": { "alternate_screen": { - "description": "Controls whether the TUI uses the terminal's alternate screen buffer.\n\n- `auto` (default): Disable alternate screen in Zellij, enable elsewhere. - `always`: Always use alternate screen (original behavior). - `never`: Never use alternate screen (inline mode only, preserves scrollback).\n\nUsing alternate screen provides a cleaner fullscreen experience but prevents scrollback in terminal multiplexers like Zellij that follow the xterm spec.", - "default": "auto", "allOf": [ { "$ref": "#/definitions/AltScreenMode" } - ] + ], + "default": "auto", + "description": "Controls whether the TUI uses the terminal's alternate screen buffer.\n\n- `auto` (default): Disable alternate screen in Zellij, enable elsewhere. - `always`: Always use alternate screen (original behavior). - `never`: Never use alternate screen (inline mode only, preserves scrollback).\n\nUsing alternate screen provides a cleaner fullscreen experience but prevents scrollback in terminal multiplexers like Zellij that follow the xterm spec." }, "animations": { - "description": "Enable animations (welcome screen, shimmer effects, spinners). Defaults to `true`.", "default": true, + "description": "Enable animations (welcome screen, shimmer effects, spinners). Defaults to `true`.", "type": "boolean" }, "notifications": { - "description": "Enable desktop notifications from the TUI when the terminal is unfocused. Defaults to `true`.", - "default": true, "allOf": [ { "$ref": "#/definitions/Notifications" } - ] + ], + "default": true, + "description": "Enable desktop notifications from the TUI when the terminal is unfocused. Defaults to `true`." }, "scroll_events_per_tick": { "description": "Override the *wheel* event density used to normalize TUI2 scrolling.\n\nTerminals generally deliver both mouse wheels and trackpads as discrete `scroll up/down` mouse events with direction but no magnitude. Unfortunately, the *number* of raw events per physical wheel notch varies by terminal (commonly 1, 3, or 9+). TUI2 uses this value to normalize that raw event density into consistent \"wheel tick\" behavior.\n\nWheel math (conceptually):\n\n- A single event contributes `1 / scroll_events_per_tick` tick-equivalents. - Wheel-like streams then scale that by `scroll_wheel_lines` so one physical notch scrolls a fixed number of lines.\n\nTrackpad math is intentionally *not* fully tied to this value: in trackpad-like mode, TUI2 uses `min(scroll_events_per_tick, 3)` as the divisor so terminals with dense wheel ticks (e.g. 9 events per notch) do not make trackpads feel artificially slow.\n\nDefaults are derived per terminal from [`crate::terminal::TerminalInfo`] when TUI2 starts. See `codex-rs/tui2/docs/scroll_input_model.md` for the probe data and rationale.", - "type": "integer", "format": "uint16", - "minimum": 0.0 + "minimum": 0.0, + "type": "integer" }, "scroll_invert": { - "description": "Invert mouse scroll direction in TUI2.\n\nThis flips the scroll sign after terminal detection. It is applied consistently to both wheel and trackpad input.", "default": false, + "description": "Invert mouse scroll direction in TUI2.\n\nThis flips the scroll sign after terminal detection. It is applied consistently to both wheel and trackpad input.", "type": "boolean" }, "scroll_mode": { - "description": "Select how TUI2 interprets mouse scroll input.\n\n- `auto` (default): infer wheel vs trackpad per scroll stream. - `wheel`: always use wheel behavior (fixed lines per wheel notch). - `trackpad`: always use trackpad behavior (fractional accumulation; wheel may feel slow).", - "default": "auto", "allOf": [ { "$ref": "#/definitions/ScrollInputMode" } - ] + ], + "default": "auto", + "description": "Select how TUI2 interprets mouse scroll input.\n\n- `auto` (default): infer wheel vs trackpad per scroll stream. - `wheel`: always use wheel behavior (fixed lines per wheel notch). - `trackpad`: always use trackpad behavior (fractional accumulation; wheel may feel slow)." }, "scroll_trackpad_accel_events": { "description": "Trackpad acceleration: approximate number of events required to gain +1x speed in TUI2.\n\nThis keeps small swipes precise while allowing large/faster swipes to cover more content. Defaults are chosen to address terminals where trackpad event density is comparatively low.\n\nConcretely, TUI2 computes an acceleration multiplier for trackpad-like streams:\n\n- `multiplier = clamp(1 + abs(events) / scroll_trackpad_accel_events, 1..scroll_trackpad_accel_max)`\n\nThe multiplier is applied to the stream’s computed line delta (including any carried fractional remainder).", - "type": "integer", "format": "uint16", - "minimum": 0.0 + "minimum": 0.0, + "type": "integer" }, "scroll_trackpad_accel_max": { "description": "Trackpad acceleration: maximum multiplier applied to trackpad-like streams.\n\nSet to 1 to effectively disable trackpad acceleration.\n\nSee [`Tui::scroll_trackpad_accel_events`] for the exact multiplier formula.", - "type": "integer", "format": "uint16", - "minimum": 0.0 + "minimum": 0.0, + "type": "integer" }, "scroll_trackpad_lines": { "description": "Override baseline trackpad scroll sensitivity in TUI2.\n\nTrackpads do not have discrete notches, but terminals still emit discrete `scroll up/down` events. In trackpad-like mode, TUI2 accumulates fractional scroll and only applies whole lines to the viewport.\n\nTrackpad per-event contribution is:\n\n- `scroll_trackpad_lines / min(scroll_events_per_tick, 3)`\n\n(plus optional bounded acceleration; see `scroll_trackpad_accel_*`). The `min(..., 3)` divisor is deliberate: `scroll_events_per_tick` is calibrated from *wheel* behavior and can be much larger than trackpad event density, which would otherwise make trackpads feel too slow in dense-wheel terminals.\n\nDefaults to 1, meaning one tick-equivalent maps to one transcript line.", - "type": "integer", "format": "uint16", - "minimum": 0.0 + "minimum": 0.0, + "type": "integer" }, "scroll_wheel_like_max_duration_ms": { "description": "Auto-mode fallback: maximum duration (ms) that a very small stream is still treated as wheel-like.\n\nThis is only used when `scroll_events_per_tick` is effectively 1 (one event per wheel notch). In that case, we cannot observe a \"tick completion time\", so TUI2 treats a short-lived, small stream (<= 2 events) as wheel-like to preserve classic wheel behavior.", - "type": "integer", "format": "uint64", - "minimum": 0.0 + "minimum": 0.0, + "type": "integer" }, "scroll_wheel_lines": { "description": "Override how many transcript lines one physical *wheel notch* should scroll in TUI2.\n\nThis is the \"classic feel\" knob. Defaults to 3.\n\nWheel-like per-event contribution is `scroll_wheel_lines / scroll_events_per_tick`. For example, in a terminal that emits 9 events per notch, the default `3 / 9` yields 1/3 of a line per event and totals 3 lines once the full notch burst arrives.\n\nSee `codex-rs/tui2/docs/scroll_input_model.md` for details on the stream model and the wheel/trackpad heuristic.", - "type": "integer", "format": "uint16", - "minimum": 0.0 + "minimum": 0.0, + "type": "integer" }, "scroll_wheel_tick_detect_max_ms": { "description": "Auto-mode threshold: maximum time (ms) for the first tick-worth of events to arrive.\n\nIn `scroll_mode = \"auto\"`, TUI2 starts a stream as trackpad-like (to avoid overshoot) and promotes it to wheel-like if `scroll_events_per_tick` events arrive \"quickly enough\". This threshold controls what \"quickly enough\" means.\n\nMost users should leave this unset; it is primarily for terminals that emit wheel ticks batched over longer time spans.", - "type": "integer", "format": "uint64", - "minimum": 0.0 + "minimum": 0.0, + "type": "integer" }, "show_tooltips": { - "description": "Show startup tooltips in the TUI welcome screen. Defaults to `true`.", "default": true, + "description": "Show startup tooltips in the TUI welcome screen. Defaults to `true`.", "type": "boolean" } }, - "additionalProperties": false + "type": "object" }, "UriBasedFileOpener": { "oneOf": [ { - "type": "string", "enum": [ "vscode", "vscode-insiders", "windsurf", "cursor" - ] + ], + "type": "string" }, { "description": "Option to disable the URI-based file opener.", - "type": "string", "enum": [ "none" - ] + ], + "type": "string" } ] }, "Verbosity": { "description": "Controls output length/detail on GPT-5 models via the Responses API. Serialized with lowercase values to match the OpenAI API.", - "type": "string", "enum": [ "low", "medium", "high" - ] + ], + "type": "string" }, "WebSearchMode": { - "type": "string", "enum": [ "disabled", "cached", "live" - ] + ], + "type": "string" }, "WireApi": { "description": "Wire protocol that the provider speaks. Most third-party services only implement the classic OpenAI Chat Completions JSON schema, whereas OpenAI itself (and a handful of others) additionally expose the more modern *Responses* API. The two protocols use different request/response shapes and *cannot* be auto-detected at runtime, therefore each provider entry must declare which one it expects.", "oneOf": [ { "description": "The Responses API exposed by OpenAI at `/v1/responses`.", - "type": "string", "enum": [ "responses" - ] + ], + "type": "string" }, { "description": "Experimental: Responses API over WebSocket transport.", - "type": "string", "enum": [ "responses_websocket" - ] + ], + "type": "string" }, { "description": "Regular Chat Completions compatible with `/v1/chat/completions`.", - "type": "string", "enum": [ "chat" - ] + ], + "type": "string" } ] } - } + }, + "description": "Base config deserialized from ~/.codex/config.toml.", + "properties": { + "agents": { + "allOf": [ + { + "$ref": "#/definitions/AgentsToml" + } + ], + "description": "Agent-related settings (thread limits, etc.)." + }, + "analytics": { + "allOf": [ + { + "$ref": "#/definitions/AnalyticsConfigToml" + } + ], + "description": "When `false`, disables analytics across Codex product surfaces in this machine. Defaults to `true`." + }, + "approval_policy": { + "allOf": [ + { + "$ref": "#/definitions/AskForApproval" + } + ], + "description": "Default approval policy for executing commands." + }, + "chatgpt_base_url": { + "description": "Base URL for requests to ChatGPT (as opposed to the OpenAI API).", + "type": "string" + }, + "check_for_update_on_startup": { + "description": "When `true`, checks for Codex updates on startup and surfaces update prompts. Set to `false` only if your Codex updates are centrally managed. Defaults to `true`.", + "type": "boolean" + }, + "cli_auth_credentials_store": { + "allOf": [ + { + "$ref": "#/definitions/AuthCredentialsStoreMode" + } + ], + "default": null, + "description": "Preferred backend for storing CLI auth credentials. file (default): Use a file in the Codex home directory. keyring: Use an OS-specific keyring service. auto: Use the keyring if available, otherwise use a file." + }, + "compact_prompt": { + "description": "Compact prompt used for history compaction.", + "type": "string" + }, + "developer_instructions": { + "default": null, + "description": "Developer instructions inserted as a `developer` role message.", + "type": "string" + }, + "disable_paste_burst": { + "description": "When true, disables burst-paste detection for typed input entirely. All characters are inserted as they are received, and no buffering or placeholder replacement will occur for fast keypress bursts.", + "type": "boolean" + }, + "experimental_compact_prompt_file": { + "$ref": "#/definitions/AbsolutePathBuf" + }, + "experimental_use_freeform_apply_patch": { + "type": "boolean" + }, + "experimental_use_unified_exec_tool": { + "type": "boolean" + }, + "features": { + "additionalProperties": false, + "default": null, + "description": "Centralized feature flags (new). Prefer this over individual toggles.", + "properties": { + "apply_patch_freeform": { + "type": "boolean" + }, + "child_agents_md": { + "type": "boolean" + }, + "collab": { + "type": "boolean" + }, + "collaboration_modes": { + "type": "boolean" + }, + "elevated_windows_sandbox": { + "type": "boolean" + }, + "enable_experimental_windows_sandbox": { + "type": "boolean" + }, + "enable_request_compression": { + "type": "boolean" + }, + "exec_policy": { + "type": "boolean" + }, + "experimental_use_freeform_apply_patch": { + "type": "boolean" + }, + "experimental_use_unified_exec_tool": { + "type": "boolean" + }, + "experimental_windows_sandbox": { + "type": "boolean" + }, + "include_apply_patch_tool": { + "type": "boolean" + }, + "powershell_utf8": { + "type": "boolean" + }, + "remote_compaction": { + "type": "boolean" + }, + "remote_models": { + "type": "boolean" + }, + "responses_websockets": { + "type": "boolean" + }, + "shell_snapshot": { + "type": "boolean" + }, + "shell_tool": { + "type": "boolean" + }, + "steer": { + "type": "boolean" + }, + "tui2": { + "type": "boolean" + }, + "undo": { + "type": "boolean" + }, + "unified_exec": { + "type": "boolean" + }, + "web_search": { + "type": "boolean" + }, + "web_search_cached": { + "type": "boolean" + }, + "web_search_request": { + "type": "boolean" + } + }, + "type": "object" + }, + "feedback": { + "allOf": [ + { + "$ref": "#/definitions/FeedbackConfigToml" + } + ], + "description": "When `false`, disables feedback collection across Codex product surfaces. Defaults to `true`." + }, + "file_opener": { + "allOf": [ + { + "$ref": "#/definitions/UriBasedFileOpener" + } + ], + "description": "Optional URI-based file opener. If set, citations to files in the model output will be hyperlinked using the specified URI scheme." + }, + "forced_chatgpt_workspace_id": { + "default": null, + "description": "When set, restricts ChatGPT login to a specific workspace identifier.", + "type": "string" + }, + "forced_login_method": { + "allOf": [ + { + "$ref": "#/definitions/ForcedLoginMethod" + } + ], + "default": null, + "description": "When set, restricts the login mechanism users may use." + }, + "ghost_snapshot": { + "allOf": [ + { + "$ref": "#/definitions/GhostSnapshotToml" + } + ], + "default": null, + "description": "Settings for ghost snapshots (used for undo)." + }, + "hide_agent_reasoning": { + "description": "When set to `true`, `AgentReasoning` events will be hidden from the UI/output. Defaults to `false`.", + "type": "boolean" + }, + "history": { + "allOf": [ + { + "$ref": "#/definitions/History" + } + ], + "default": null, + "description": "Settings that govern if and what will be written to `~/.codex/history.jsonl`." + }, + "instructions": { + "description": "System instructions.", + "type": "string" + }, + "mcp_oauth_callback_port": { + "description": "Optional fixed port for the local HTTP callback server used during MCP OAuth login. When unset, Codex will bind to an ephemeral port chosen by the OS.", + "format": "uint16", + "minimum": 0.0, + "type": "integer" + }, + "mcp_oauth_credentials_store": { + "allOf": [ + { + "$ref": "#/definitions/OAuthCredentialsStoreMode" + } + ], + "default": null, + "description": "Preferred backend for storing MCP OAuth credentials. keyring: Use an OS-specific keyring service. https://github.com/openai/codex/blob/main/codex-rs/rmcp-client/src/oauth.rs#L2 file: Use a file in the Codex home directory. auto (default): Use the OS-specific keyring service if available, otherwise use a file." + }, + "mcp_servers": { + "additionalProperties": { + "$ref": "#/definitions/RawMcpServerConfig" + }, + "default": {}, + "description": "Definition for MCP servers that Codex can reach out to for tool calls.", + "type": "object" + }, + "model": { + "description": "Optional override of model selection.", + "type": "string" + }, + "model_auto_compact_token_limit": { + "description": "Token usage threshold triggering auto-compaction of conversation history.", + "format": "int64", + "type": "integer" + }, + "model_context_window": { + "description": "Size of the context window for the model, in tokens.", + "format": "int64", + "type": "integer" + }, + "model_instructions_file": { + "allOf": [ + { + "$ref": "#/definitions/AbsolutePathBuf" + } + ], + "description": "Optional path to a file containing model instructions that will override the built-in instructions for the selected model. Users are STRONGLY DISCOURAGED from using this field, as deviating from the instructions sanctioned by Codex will likely degrade model performance." + }, + "model_personality": { + "allOf": [ + { + "$ref": "#/definitions/Personality" + } + ], + "description": "EXPERIMENTAL Optionally specify a personality for the model" + }, + "model_provider": { + "description": "Provider to use from the model_providers map.", + "type": "string" + }, + "model_providers": { + "additionalProperties": { + "$ref": "#/definitions/ModelProviderInfo" + }, + "default": {}, + "description": "User-defined provider entries that extend/override the built-in list.", + "type": "object" + }, + "model_reasoning_effort": { + "$ref": "#/definitions/ReasoningEffort" + }, + "model_reasoning_summary": { + "$ref": "#/definitions/ReasoningSummary" + }, + "model_supports_reasoning_summaries": { + "description": "Override to force-enable reasoning summaries for the configured model.", + "type": "boolean" + }, + "model_verbosity": { + "allOf": [ + { + "$ref": "#/definitions/Verbosity" + } + ], + "description": "Optional verbosity control for GPT-5 models (Responses API `text.verbosity`)." + }, + "notice": { + "allOf": [ + { + "$ref": "#/definitions/Notice" + } + ], + "description": "Collection of in-product notices (different from notifications) See [`crate::config::types::Notices`] for more details" + }, + "notify": { + "default": null, + "description": "Optional external command to spawn for end-user notifications.", + "items": { + "type": "string" + }, + "type": "array" + }, + "oss_provider": { + "description": "Preferred OSS provider for local models, e.g. \"lmstudio\", \"ollama\", or \"ollama-chat\".", + "type": "string" + }, + "otel": { + "allOf": [ + { + "$ref": "#/definitions/OtelConfigToml" + } + ], + "description": "OTEL configuration." + }, + "profile": { + "description": "Profile to use from the `profiles` map.", + "type": "string" + }, + "profiles": { + "additionalProperties": { + "$ref": "#/definitions/ConfigProfile" + }, + "default": {}, + "description": "Named profiles to facilitate switching between different configurations.", + "type": "object" + }, + "project_doc_fallback_filenames": { + "description": "Ordered list of fallback filenames to look for when AGENTS.md is missing.", + "items": { + "type": "string" + }, + "type": "array" + }, + "project_doc_max_bytes": { + "description": "Maximum number of bytes to include from an AGENTS.md project doc file.", + "format": "uint", + "minimum": 0.0, + "type": "integer" + }, + "project_root_markers": { + "default": null, + "description": "Markers used to detect the project root when searching parent directories for `.codex` folders. Defaults to [\".git\"] when unset.", + "items": { + "type": "string" + }, + "type": "array" + }, + "projects": { + "additionalProperties": { + "$ref": "#/definitions/ProjectConfig" + }, + "type": "object" + }, + "review_model": { + "description": "Review model override used by the `/review` feature.", + "type": "string" + }, + "sandbox_mode": { + "allOf": [ + { + "$ref": "#/definitions/SandboxMode" + } + ], + "description": "Sandbox mode to use." + }, + "sandbox_workspace_write": { + "allOf": [ + { + "$ref": "#/definitions/SandboxWorkspaceWrite" + } + ], + "description": "Sandbox configuration to apply if `sandbox` is `WorkspaceWrite`." + }, + "shell_environment_policy": { + "allOf": [ + { + "$ref": "#/definitions/ShellEnvironmentPolicyToml" + } + ], + "default": { + "exclude": null, + "experimental_use_profile": null, + "ignore_default_excludes": null, + "include_only": null, + "inherit": null, + "set": null + } + }, + "show_raw_agent_reasoning": { + "description": "When set to `true`, `AgentReasoningRawContentEvent` events will be shown in the UI/output. Defaults to `false`.", + "type": "boolean" + }, + "skills": { + "allOf": [ + { + "$ref": "#/definitions/SkillsConfig" + } + ], + "description": "User-level skill config entries keyed by SKILL.md path." + }, + "tool_output_token_limit": { + "description": "Token budget applied when storing tool/function outputs in the context manager.", + "format": "uint", + "minimum": 0.0, + "type": "integer" + }, + "tools": { + "allOf": [ + { + "$ref": "#/definitions/ToolsToml" + } + ], + "description": "Nested tools section for feature toggles" + }, + "tui": { + "allOf": [ + { + "$ref": "#/definitions/Tui" + } + ], + "description": "Collection of settings that are specific to the TUI." + }, + "web_search": { + "allOf": [ + { + "$ref": "#/definitions/WebSearchMode" + } + ], + "description": "Controls the web search tool mode: disabled, cached, or live." + }, + "windows_wsl_setup_acknowledged": { + "description": "Tracks whether the Windows onboarding screen has been acknowledged.", + "type": "boolean" + } + }, + "title": "ConfigToml", + "type": "object" } \ No newline at end of file diff --git a/codex-rs/core/src/config/schema.rs b/codex-rs/core/src/config/schema.rs index 674dca3aa..0d54c1391 100644 --- a/codex-rs/core/src/config/schema.rs +++ b/codex-rs/core/src/config/schema.rs @@ -8,6 +8,8 @@ use schemars::schema::ObjectValidation; use schemars::schema::RootSchema; use schemars::schema::Schema; use schemars::schema::SchemaObject; +use serde_json::Map; +use serde_json::Value; use std::path::Path; /// Schema for the `[features]` map with known + legacy keys only. @@ -60,10 +62,29 @@ pub fn config_schema() -> RootSchema { .into_root_schema_for::() } +/// Canonicalize a JSON value by sorting its keys. +fn canonicalize(value: &Value) -> Value { + match value { + Value::Array(items) => Value::Array(items.iter().map(canonicalize).collect()), + Value::Object(map) => { + let mut entries: Vec<_> = map.iter().collect(); + entries.sort_by(|(left, _), (right, _)| left.cmp(right)); + let mut sorted = Map::with_capacity(map.len()); + for (key, child) in entries { + sorted.insert(key.clone(), canonicalize(child)); + } + Value::Object(sorted) + } + _ => value.clone(), + } +} + /// Render the config schema as pretty-printed JSON. pub fn config_schema_json() -> anyhow::Result> { let schema = config_schema(); - let json = serde_json::to_vec_pretty(&schema)?; + let value = serde_json::to_value(schema)?; + let value = canonicalize(&value); + let json = serde_json::to_vec_pretty(&value)?; Ok(json) } @@ -76,26 +97,10 @@ pub fn write_config_schema(out_path: &Path) -> anyhow::Result<()> { #[cfg(test)] mod tests { + use super::canonicalize; use super::config_schema_json; - use serde_json::Map; - use serde_json::Value; - use similar::TextDiff; - fn canonicalize(value: &Value) -> Value { - match value { - Value::Array(items) => Value::Array(items.iter().map(canonicalize).collect()), - Value::Object(map) => { - let mut entries: Vec<_> = map.iter().collect(); - entries.sort_by(|(left, _), (right, _)| left.cmp(right)); - let mut sorted = Map::with_capacity(map.len()); - for (key, child) in entries { - sorted.insert(key.clone(), canonicalize(child)); - } - Value::Object(sorted) - } - _ => value.clone(), - } - } + use similar::TextDiff; #[test] fn config_schema_matches_fixture() {