From 131b60dd4bae1f3c681353b4f5345dfa128e13a0 Mon Sep 17 00:00:00 2001 From: Sayan Sisodiya Date: Tue, 20 Jan 2026 17:15:33 -0800 Subject: [PATCH 1/9] publish config schema on release --- .github/workflows/rust-release.yml | 13 + codex-rs/core/config.schema.json | 929 +++++++++++++++++++++-------- codex-rs/core/src/config/schema.rs | 43 +- 3 files changed, 715 insertions(+), 270 deletions(-) diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml index acc8b6bafe94..99519334b93c 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.90 - name: Validate tag matches Cargo.toml version shell: bash @@ -45,6 +46,14 @@ 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 + 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 +367,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: | diff --git a/codex-rs/core/config.schema.json b/codex-rs/core/config.schema.json index e9ec0d7e65e9..45739d7b4148 100644 --- a/codex-rs/core/config.schema.json +++ b/codex-rs/core/config.schema.json @@ -461,68 +461,68 @@ "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 +531,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 +575,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 +655,7 @@ "type": "boolean" } }, - "additionalProperties": false + "type": "object" }, "include_apply_patch_tool": { "type": "boolean" @@ -703,27 +703,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 +731,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 +812,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 +892,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 +908,10 @@ "type": "boolean" }, { - "type": "array", "items": { "type": "string" - } + }, + "type": "array" } ] }, @@ -920,143 +920,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 - }, - { - "type": "object", "required": [ - "otlp-grpc" + "otlp-http" ], + "type": "object" + }, + { + "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 +1064,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 +1091,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 +1134,10 @@ }, "disabled_tools": { "default": null, - "type": "array", "items": { "type": "string" - } + }, + "type": "array" }, "enabled": { "default": null, @@ -1145,63 +1145,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 +1208,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 +1256,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 +1294,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 +1334,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 +1361,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 +1392,596 @@ "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": { + "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_instructions_file": { + "allOf": [ + { + "$ref": "#/definitions/AbsolutePathBuf" + } + ], + "description": "Legacy, now use features" + }, + "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" + }, + "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_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 674dca3aafef..0d54c1391358 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() { From 05d8a8d228eb9d9ebbee203fd272388595a4ccc2 Mon Sep 17 00:00:00 2001 From: Sayan Sisodiya Date: Tue, 20 Jan 2026 17:28:06 -0800 Subject: [PATCH 2/9] bump to latest rust ver --- .github/workflows/rust-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml index 99519334b93c..f07aea352db9 100644 --- a/.github/workflows/rust-release.yml +++ b/.github/workflows/rust-release.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - - uses: dtolnay/rust-toolchain@1.90 + - uses: dtolnay/rust-toolchain@1.92 - name: Validate tag matches Cargo.toml version shell: bash From b2932b474f7e72785c4a4d3e854e4b2ffbfa6f56 Mon Sep 17 00:00:00 2001 From: Sayan Sisodiya Date: Tue, 20 Jan 2026 20:19:00 -0800 Subject: [PATCH 3/9] trigger dev site redeploy on release to update config schema --- .github/workflows/rust-release.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml index f07aea352db9..651f51e1f763 100644 --- a/.github/workflows/rust-release.yml +++ b/.github/workflows/rust-release.yml @@ -434,6 +434,15 @@ jobs: # (e.g. -alpha, -beta). Otherwise publish a normal release. prerelease: ${{ contains(steps.release_name.outputs.name, '-') }} + - 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: ${{ success() && !contains(steps.release_name.outputs.name, '-') }} + env: + DEV_WEBSITE_VERCEL_DEPLOY_HOOK_URL: ${{ secrets.DEV_WEBSITE_VERCEL_DEPLOY_HOOK_URL }} + run: | + curl -sS -X POST "$DEV_WEBSITE_VERCEL_DEPLOY_HOOK_URL" + - uses: facebook/dotslash-publish-release@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 6db82c79c2d03022a087c7d55c1926e5eb088323 Mon Sep 17 00:00:00 2001 From: Sayan Sisodiya Date: Tue, 20 Jan 2026 20:32:29 -0800 Subject: [PATCH 4/9] fail on 4xx/5xx responses to vercel webhook post --- .github/workflows/rust-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml index 651f51e1f763..3c3b9a4d7710 100644 --- a/.github/workflows/rust-release.yml +++ b/.github/workflows/rust-release.yml @@ -441,7 +441,7 @@ jobs: env: DEV_WEBSITE_VERCEL_DEPLOY_HOOK_URL: ${{ secrets.DEV_WEBSITE_VERCEL_DEPLOY_HOOK_URL }} run: | - curl -sS -X POST "$DEV_WEBSITE_VERCEL_DEPLOY_HOOK_URL" + curl --fail --show-error --silent -X POST "$DEV_WEBSITE_VERCEL_DEPLOY_HOOK_URL" - uses: facebook/dotslash-publish-release@v2 env: From 91ae1894225d86607326a62ba499cd8e5aceff5d Mon Sep 17 00:00:00 2001 From: Sayan Sisodiya Date: Wed, 21 Jan 2026 14:04:24 -0800 Subject: [PATCH 5/9] suppress response body in curl --- .github/workflows/rust-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml index 3c3b9a4d7710..5d836058253e 100644 --- a/.github/workflows/rust-release.yml +++ b/.github/workflows/rust-release.yml @@ -441,7 +441,7 @@ jobs: env: DEV_WEBSITE_VERCEL_DEPLOY_HOOK_URL: ${{ secrets.DEV_WEBSITE_VERCEL_DEPLOY_HOOK_URL }} run: | - curl --fail --show-error --silent -X POST "$DEV_WEBSITE_VERCEL_DEPLOY_HOOK_URL" + curl -sS -f -o /dev/null -X POST "$DEV_WEBSITE_VERCEL_DEPLOY_HOOK_URL" - uses: facebook/dotslash-publish-release@v2 env: From 59b74606ee7bf21333af3267d04f28a24c6036a9 Mon Sep 17 00:00:00 2001 From: Sayan Sisodiya Date: Wed, 21 Jan 2026 15:07:45 -0800 Subject: [PATCH 6/9] add clarifying echo to CI step --- .github/workflows/rust-release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml index 5d836058253e..eb1ffcf244d0 100644 --- a/.github/workflows/rust-release.yml +++ b/.github/workflows/rust-release.yml @@ -51,6 +51,7 @@ jobs: 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 From dd02d4c284e6d7ca726eecf797a482151b40d7c6 Mon Sep 17 00:00:00 2001 From: Sayan Sisodiya Date: Wed, 21 Jan 2026 15:42:35 -0800 Subject: [PATCH 7/9] dont fail release on website deploy failure, but emit warning --- .github/workflows/rust-release.yml | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml index eb1ffcf244d0..a7cbca0c5f3a 100644 --- a/.github/workflows/rust-release.yml +++ b/.github/workflows/rust-release.yml @@ -435,15 +435,6 @@ jobs: # (e.g. -alpha, -beta). Otherwise publish a normal release. prerelease: ${{ contains(steps.release_name.outputs.name, '-') }} - - 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: ${{ success() && !contains(steps.release_name.outputs.name, '-') }} - env: - DEV_WEBSITE_VERCEL_DEPLOY_HOOK_URL: ${{ secrets.DEV_WEBSITE_VERCEL_DEPLOY_HOOK_URL }} - run: | - curl -sS -f -o /dev/null -X POST "$DEV_WEBSITE_VERCEL_DEPLOY_HOOK_URL" - - uses: facebook/dotslash-publish-release@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -451,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 From 5c2451ff35e6f7668814dc82763a3c95a0c3a454 Mon Sep 17 00:00:00 2001 From: Sayan Sisodiya Date: Wed, 21 Jan 2026 16:07:07 -0800 Subject: [PATCH 8/9] unify schema after rebase --- codex-rs/core/config.schema.json | 477 ++----------------------------- 1 file changed, 25 insertions(+), 452 deletions(-) diff --git a/codex-rs/core/config.schema.json b/codex-rs/core/config.schema.json index 45739d7b4148..92e36bd79e00 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,16 +7,16 @@ "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.", @@ -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" @@ -1559,6 +1121,14 @@ }, "description": "Base config deserialized from ~/.codex/config.toml.", "properties": { + "agents": { + "allOf": [ + { + "$ref": "#/definitions/AgentsToml" + } + ], + "description": "Agent-related settings (thread limits, etc.)." + }, "analytics": { "allOf": [ { @@ -1608,14 +1178,6 @@ "experimental_compact_prompt_file": { "$ref": "#/definitions/AbsolutePathBuf" }, - "experimental_instructions_file": { - "allOf": [ - { - "$ref": "#/definitions/AbsolutePathBuf" - } - ], - "description": "Legacy, now use features" - }, "experimental_use_freeform_apply_patch": { "type": "boolean" }, @@ -1672,6 +1234,9 @@ "remote_models": { "type": "boolean" }, + "responses_websockets": { + "type": "boolean" + }, "shell_snapshot": { "type": "boolean" }, @@ -1795,6 +1360,14 @@ "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": [ { From d2f971283252fd628977c94851f38e0d68d77781 Mon Sep 17 00:00:00 2001 From: Sayan Sisodiya Date: Wed, 21 Jan 2026 16:11:05 -0800 Subject: [PATCH 9/9] prettier --- .github/workflows/rust-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml index a7cbca0c5f3a..f058f8dbef3b 100644 --- a/.github/workflows/rust-release.yml +++ b/.github/workflows/rust-release.yml @@ -443,8 +443,8 @@ jobs: 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. + # 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: