Merge upstream/main: sync upstream, keep local workflows

This commit is contained in:
James Peter
2025-12-10 07:48:21 +10:00
12 changed files with 425 additions and 184 deletions
+1
View File
@@ -1 +1,2 @@
iTerm
psuedo
@@ -0,0 +1,44 @@
name: linux-code-sign
description: Sign Linux artifacts with cosign.
inputs:
target:
description: Target triple for the artifacts to sign.
required: true
artifacts-dir:
description: Absolute path to the directory containing built binaries to sign.
required: true
runs:
using: composite
steps:
- name: Install cosign
uses: sigstore/[email protected]
- name: Cosign Linux artifacts
shell: bash
env:
COSIGN_EXPERIMENTAL: "1"
COSIGN_YES: "true"
COSIGN_OIDC_CLIENT_ID: "sigstore"
COSIGN_OIDC_ISSUER: "https://oauth2.sigstore.dev/auth"
run: |
set -euo pipefail
dest="${{ inputs.artifacts-dir }}"
if [[ ! -d "$dest" ]]; then
echo "Destination $dest does not exist"
exit 1
fi
for binary in codex codex-responses-api-proxy; do
artifact="${dest}/${binary}"
if [[ ! -f "$artifact" ]]; then
echo "Binary $artifact not found"
exit 1
fi
cosign sign-blob \
--yes \
--bundle "${artifact}.sigstore" \
"$artifact"
done
+53 -53
View File
@@ -47,27 +47,27 @@ const ALL_TEXT_VERBOSITY: &[TextVerbosityConfig] = &[
static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
vec![
ModelPreset {
id: "gpt-5.1-codex-max",
model: "gpt-5.1-codex-max",
display_name: "gpt-5.1-codex-max",
description: "Latest Codex-optimized flagship for deep and fast reasoning.",
id: "gpt-5.1-codex-max".to_string(),
model: "gpt-5.1-codex-max".to_string(),
display_name: "gpt-5.1-codex-max".to_string(),
description: "Latest Codex-optimized flagship for deep and fast reasoning.".to_string(),
default_reasoning_effort: ReasoningEffort::Medium,
supported_reasoning_efforts: &[
supported_reasoning_efforts: vec![
ReasoningEffortPreset {
effort: ReasoningEffort::Low,
description: "Fast responses with lighter reasoning",
description: "Fast responses with lighter reasoning".to_string(),
},
ReasoningEffortPreset {
effort: ReasoningEffort::Medium,
description: "Balances speed and reasoning depth for everyday tasks",
description: "Balances speed and reasoning depth for everyday tasks".to_string(),
},
ReasoningEffortPreset {
effort: ReasoningEffort::High,
description: "Maximizes reasoning depth for complex problems",
description: "Maximizes reasoning depth for complex problems".to_string(),
},
ReasoningEffortPreset {
effort: ReasoningEffort::XHigh,
description: "Extra high reasoning depth for complex problems",
description: "Extra high reasoning depth for complex problems".to_string(),
},
],
supported_text_verbosity: &[TextVerbosityConfig::Medium],
@@ -81,26 +81,27 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
display_name: "gpt-5.1-codex",
description: "Optimized for Code.",
default_reasoning_effort: ReasoningEffort::Medium,
supported_reasoning_efforts: &[
supported_reasoning_efforts: vec![
ReasoningEffortPreset {
effort: ReasoningEffort::Low,
description: "Fastest responses with limited reasoning",
description: "Fastest responses with limited reasoning".to_string(),
},
ReasoningEffortPreset {
effort: ReasoningEffort::Medium,
description: "Dynamically adjusts reasoning based on the task",
description: "Dynamically adjusts reasoning based on the task".to_string(),
},
ReasoningEffortPreset {
effort: ReasoningEffort::High,
description: "Maximizes reasoning depth for complex or ambiguous problems",
description: "Maximizes reasoning depth for complex or ambiguous problems"
.to_string(),
},
],
supported_text_verbosity: ALL_TEXT_VERBOSITY,
is_default: false,
upgrade: Some(ModelUpgrade {
id: "gpt-5.1-codex-max",
id: "gpt-5.1-codex-max".to_string(),
reasoning_effort_mapping: None,
migration_config_key: HIDE_GPT_5_1_CODEX_MAX_MIGRATION_PROMPT_CONFIG,
migration_config_key: HIDE_GPT_5_1_CODEX_MAX_MIGRATION_PROMPT_CONFIG.to_string(),
}),
show_in_picker: true,
},
@@ -110,32 +111,33 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
display_name: "gpt-5.1-codex-mini",
description: "Optimized for Code. Cheaper, faster, but less capable.",
default_reasoning_effort: ReasoningEffort::Medium,
supported_reasoning_efforts: &[
supported_reasoning_efforts: vec![
ReasoningEffortPreset {
effort: ReasoningEffort::Medium,
description: "Dynamically adjusts reasoning based on the task",
description: "Dynamically adjusts reasoning based on the task".to_string(),
},
ReasoningEffortPreset {
effort: ReasoningEffort::High,
description: "Maximizes reasoning depth for complex or ambiguous problems",
description: "Maximizes reasoning depth for complex or ambiguous problems"
.to_string(),
},
],
supported_text_verbosity: ALL_TEXT_VERBOSITY,
is_default: false,
upgrade: Some(ModelUpgrade {
id: "gpt-5.1-codex-max",
id: "gpt-5.1-codex-max".to_string(),
reasoning_effort_mapping: None,
migration_config_key: HIDE_GPT_5_1_CODEX_MAX_MIGRATION_PROMPT_CONFIG,
migration_config_key: HIDE_GPT_5_1_CODEX_MAX_MIGRATION_PROMPT_CONFIG.to_string(),
}),
show_in_picker: true,
},
ModelPreset {
id: "gpt-5.1",
model: "gpt-5.1",
display_name: "gpt-5.1",
description: "Broad world knowledge with strong general reasoning.",
id: "gpt-5.1".to_string(),
model: "gpt-5.1".to_string(),
display_name: "gpt-5.1".to_string(),
description: "Broad world knowledge with strong general reasoning.".to_string(),
default_reasoning_effort: ReasoningEffort::Medium,
supported_reasoning_efforts: &[
supported_reasoning_efforts: vec![
ReasoningEffortPreset {
effort: ReasoningEffort::Low,
description:
@@ -148,15 +150,15 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
},
ReasoningEffortPreset {
effort: ReasoningEffort::High,
description: "Maximizes reasoning depth for complex or ambiguous problems",
description: "Maximizes reasoning depth for complex or ambiguous problems".to_string(),
},
],
supported_text_verbosity: ALL_TEXT_VERBOSITY,
is_default: false,
upgrade: Some(ModelUpgrade {
id: "gpt-5.1-codex-max",
id: "gpt-5.1-codex-max".to_string(),
reasoning_effort_mapping: None,
migration_config_key: HIDE_GPT_5_1_CODEX_MAX_MIGRATION_PROMPT_CONFIG,
migration_config_key: HIDE_GPT_5_1_CODEX_MAX_MIGRATION_PROMPT_CONFIG.to_string(),
}),
show_in_picker: true,
},
@@ -167,26 +169,26 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
display_name: "gpt-5-codex",
description: "Optimized for Code.",
default_reasoning_effort: ReasoningEffort::Medium,
supported_reasoning_efforts: &[
supported_reasoning_efforts: vec![
ReasoningEffortPreset {
effort: ReasoningEffort::Low,
description: "Fastest responses with limited reasoning",
description: "Fastest responses with limited reasoning".to_string(),
},
ReasoningEffortPreset {
effort: ReasoningEffort::Medium,
description: "Dynamically adjusts reasoning based on the task",
description: "Dynamically adjusts reasoning based on the task".to_string(),
},
ReasoningEffortPreset {
effort: ReasoningEffort::High,
description: "Maximizes reasoning depth for complex or ambiguous problems",
description: "Maximizes reasoning depth for complex or ambiguous problems".to_string(),
},
],
supported_text_verbosity: ALL_TEXT_VERBOSITY,
is_default: false,
upgrade: Some(ModelUpgrade {
id: "gpt-5.1-codex-max",
id: "gpt-5.1-codex-max".to_string(),
reasoning_effort_mapping: None,
migration_config_key: HIDE_GPT_5_1_CODEX_MAX_MIGRATION_PROMPT_CONFIG,
migration_config_key: HIDE_GPT_5_1_CODEX_MAX_MIGRATION_PROMPT_CONFIG.to_string(),
}),
show_in_picker: false,
},
@@ -196,35 +198,35 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
display_name: "gpt-5-codex-mini",
description: "Optimized for Code. Cheaper, faster, but less capable.",
default_reasoning_effort: ReasoningEffort::Medium,
supported_reasoning_efforts: &[
supported_reasoning_efforts: vec![
ReasoningEffortPreset {
effort: ReasoningEffort::Medium,
description: "Dynamically adjusts reasoning based on the task",
description: "Dynamically adjusts reasoning based on the task".to_string(),
},
ReasoningEffortPreset {
effort: ReasoningEffort::High,
description: "Maximizes reasoning depth for complex or ambiguous problems",
description: "Maximizes reasoning depth for complex or ambiguous problems".to_string(),
},
],
supported_text_verbosity: ALL_TEXT_VERBOSITY,
is_default: false,
upgrade: Some(ModelUpgrade {
id: "gpt-5.1-codex-mini",
id: "gpt-5.1-codex-mini".to_string(),
reasoning_effort_mapping: None,
migration_config_key: HIDE_GPT5_1_MIGRATION_PROMPT_CONFIG,
migration_config_key: HIDE_GPT5_1_MIGRATION_PROMPT_CONFIG.to_string(),
}),
show_in_picker: false,
},
ModelPreset {
id: "gpt-5",
model: "gpt-5",
display_name: "gpt-5",
description: "Broad world knowledge with strong general reasoning.",
id: "gpt-5".to_string(),
model: "gpt-5".to_string(),
display_name: "gpt-5".to_string(),
description: "Broad world knowledge with strong general reasoning.".to_string(),
default_reasoning_effort: ReasoningEffort::Medium,
supported_reasoning_efforts: &[
supported_reasoning_efforts: vec![
ReasoningEffortPreset {
effort: ReasoningEffort::Minimal,
description: "Fastest responses with little reasoning",
description: "Fastest responses with little reasoning".to_string(),
},
ReasoningEffortPreset {
effort: ReasoningEffort::Low,
@@ -238,32 +240,30 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
},
ReasoningEffortPreset {
effort: ReasoningEffort::High,
description: "Maximizes reasoning depth for complex or ambiguous problems",
description: "Maximizes reasoning depth for complex or ambiguous problems".to_string(),
},
],
supported_text_verbosity: ALL_TEXT_VERBOSITY,
is_default: false,
upgrade: Some(ModelUpgrade {
id: "gpt-5.1-codex-max",
id: "gpt-5.1-codex-max".to_string(),
reasoning_effort_mapping: None,
migration_config_key: HIDE_GPT_5_1_CODEX_MAX_MIGRATION_PROMPT_CONFIG,
migration_config_key: HIDE_GPT_5_1_CODEX_MAX_MIGRATION_PROMPT_CONFIG.to_string(),
}),
show_in_picker: false,
},
]
});
pub fn builtin_model_presets(auth_mode: Option<AuthMode>) -> Vec<ModelPreset> {
pub(crate) fn builtin_model_presets(_auth_mode: Option<AuthMode>) -> Vec<ModelPreset> {
PRESETS
.iter()
.filter(|preset| match auth_mode {
Some(AuthMode::ApiKey) => preset.show_in_picker && preset.id != "gpt-5.1-codex-max",
_ => preset.show_in_picker,
})
.filter(|preset| preset.show_in_picker)
.cloned()
.collect()
}
// todo(aibrahim): remove this once we migrate tests
pub fn all_model_presets() -> &'static Vec<ModelPreset> {
&PRESETS
}
-85
View File
@@ -1,85 +0,0 @@
use crate::model_family::ModelFamily;
/// Metadata about a model, particularly OpenAI models.
/// We may want to consider including details like the pricing for
/// input tokens, output tokens, etc., though users will need to be able to
/// override this in config.toml, as this information can get out of date.
/// Though this would help present more accurate pricing information in the UI.
#[derive(Debug)]
pub struct ModelInfo {
/// Size of the context window in tokens. This is the maximum size of the input context.
pub context_window: u64,
/// Maximum number of output tokens that can be generated for the model.
pub max_output_tokens: u64,
/// Token threshold where we should automatically compact conversation history. This considers
/// input tokens + output tokens of this turn.
pub auto_compact_token_limit: Option<i64>,
}
impl ModelInfo {
const fn new(context_window: u64, max_output_tokens: u64) -> Self {
Self {
context_window,
max_output_tokens,
auto_compact_token_limit: Some(Self::default_auto_compact_limit(context_window)),
}
}
const fn default_auto_compact_limit(context_window: u64) -> i64 {
// 90% of context window, matching upstream codex-rs behavior
((context_window * 9) / 10) as i64
}
}
pub fn get_model_info(model_family: &ModelFamily) -> Option<ModelInfo> {
let slug = model_family.slug.as_str();
match slug {
// OSS models have a 128k shared token pool.
// Arbitrarily splitting it: 3/4 input context, 1/4 output.
// https://openai.com/index/gpt-oss-model-card/
"gpt-oss-20b" => Some(ModelInfo::new(96_000, 32_000)),
"gpt-oss-120b" => Some(ModelInfo::new(96_000, 32_000)),
// https://platform.openai.com/docs/models/o3
"o3" => Some(ModelInfo::new(200_000, 100_000)),
// https://platform.openai.com/docs/models/o4-mini
"o4-mini" => Some(ModelInfo::new(200_000, 100_000)),
// https://platform.openai.com/docs/models/codex-mini-latest
"codex-mini-latest" => Some(ModelInfo::new(200_000, 100_000)),
// As of Jun 25, 2025, gpt-4.1 defaults to gpt-4.1-2025-04-14.
// https://platform.openai.com/docs/models/gpt-4.1
"gpt-4.1" | "gpt-4.1-2025-04-14" => Some(ModelInfo::new(1_047_576, 32_768)),
// As of Jun 25, 2025, gpt-4o defaults to gpt-4o-2024-08-06.
// https://platform.openai.com/docs/models/gpt-4o
"gpt-4o" | "gpt-4o-2024-08-06" => Some(ModelInfo::new(128_000, 16_384)),
// https://platform.openai.com/docs/models/gpt-4o?snapshot=gpt-4o-2024-05-13
"gpt-4o-2024-05-13" => Some(ModelInfo::new(128_000, 4_096)),
// https://platform.openai.com/docs/models/gpt-4o?snapshot=gpt-4o-2024-11-20
"gpt-4o-2024-11-20" => Some(ModelInfo::new(128_000, 16_384)),
// https://platform.openai.com/docs/models/gpt-3.5-turbo
"gpt-3.5-turbo" => Some(ModelInfo::new(16_385, 4_096)),
_ if slug.starts_with("gpt-5-codex")
|| slug.starts_with("gpt-5.1-codex")
|| slug.starts_with("gpt-5.1-codex-max") =>
{
Some(ModelInfo::new(272_000, 128_000))
}
_ if slug.starts_with("gpt-5.1") => Some(ModelInfo::new(272_000, 128_000)),
_ if slug.starts_with("gpt-5") => Some(ModelInfo::new(272_000, 128_000)),
_ if slug.starts_with("codex-") => Some(ModelInfo::new(272_000, 128_000)),
_ => None,
}
}
+1
View File
@@ -1,3 +1,4 @@
set working-directory := "codex-rs"
set positional-arguments
# Display help
+149 -28
View File
@@ -1,14 +1,49 @@
use codex_app_server_protocol::AuthMode;
use codex_protocol::openai_models::ModelPreset;
use codex_protocol::openai_models::ModelUpgrade;
use codex_protocol::openai_models::ReasoningEffort;
use codex_protocol::openai_models::ReasoningEffortPreset;
use std::collections::HashMap;
use code_app_server_protocol::AuthMode;
use code_core::config_types::TextVerbosity as TextVerbosityConfig;
use code_core::protocol_config_types::ReasoningEffort;
use once_cell::sync::Lazy;
pub const HIDE_GPT5_1_MIGRATION_PROMPT_CONFIG: &str = "hide_gpt5_1_migration_prompt";
pub const HIDE_GPT_5_1_CODEX_MAX_MIGRATION_PROMPT_CONFIG: &str =
"hide_gpt-5.1-codex-max_migration_prompt";
/// A reasoning effort option surfaced for a model.
#[derive(Debug, Clone, Copy)]
pub struct ReasoningEffortPreset {
pub effort: ReasoningEffort,
pub description: &'static str,
}
#[derive(Debug, Clone)]
pub struct ModelUpgrade {
pub id: &'static str,
pub reasoning_effort_mapping: Option<HashMap<ReasoningEffort, ReasoningEffort>>,
pub migration_config_key: &'static str,
}
/// Metadata describing a Code-supported model.
#[derive(Debug, Clone)]
pub struct ModelPreset {
pub id: &'static str,
pub model: &'static str,
pub display_name: &'static str,
pub description: &'static str,
pub default_reasoning_effort: ReasoningEffort,
pub supported_reasoning_efforts: &'static [ReasoningEffortPreset],
pub supported_text_verbosity: &'static [TextVerbosityConfig],
pub is_default: bool,
pub upgrade: Option<ModelUpgrade>,
pub show_in_picker: bool,
}
const ALL_TEXT_VERBOSITY: &[TextVerbosityConfig] = &[
TextVerbosityConfig::Low,
TextVerbosityConfig::Medium,
TextVerbosityConfig::High,
];
static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
vec![
ModelPreset {
@@ -35,15 +70,16 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
description: "Extra high reasoning depth for complex problems".to_string(),
},
],
supported_text_verbosity: &[TextVerbosityConfig::Medium],
is_default: true,
upgrade: None,
show_in_picker: true,
},
ModelPreset {
id: "gpt-5.1-codex".to_string(),
model: "gpt-5.1-codex".to_string(),
display_name: "gpt-5.1-codex".to_string(),
description: "Optimized for codex.".to_string(),
id: "gpt-5.1-codex",
model: "gpt-5.1-codex",
display_name: "gpt-5.1-codex",
description: "Optimized for Code.",
default_reasoning_effort: ReasoningEffort::Medium,
supported_reasoning_efforts: vec![
ReasoningEffortPreset {
@@ -60,6 +96,7 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
.to_string(),
},
],
supported_text_verbosity: ALL_TEXT_VERBOSITY,
is_default: false,
upgrade: Some(ModelUpgrade {
id: "gpt-5.1-codex-max".to_string(),
@@ -69,10 +106,10 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
show_in_picker: true,
},
ModelPreset {
id: "gpt-5.1-codex-mini".to_string(),
model: "gpt-5.1-codex-mini".to_string(),
display_name: "gpt-5.1-codex-mini".to_string(),
description: "Optimized for codex. Cheaper, faster, but less capable.".to_string(),
id: "gpt-5.1-codex-mini",
model: "gpt-5.1-codex-mini",
display_name: "gpt-5.1-codex-mini",
description: "Optimized for Code. Cheaper, faster, but less capable.",
default_reasoning_effort: ReasoningEffort::Medium,
supported_reasoning_efforts: vec![
ReasoningEffortPreset {
@@ -85,6 +122,7 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
.to_string(),
},
],
supported_text_verbosity: ALL_TEXT_VERBOSITY,
is_default: false,
upgrade: Some(ModelUpgrade {
id: "gpt-5.1-codex-max".to_string(),
@@ -102,17 +140,20 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
supported_reasoning_efforts: vec![
ReasoningEffortPreset {
effort: ReasoningEffort::Low,
description: "Balances speed with some reasoning; useful for straightforward queries and short explanations".to_string(),
description:
"Balances speed with some reasoning; useful for straightforward queries and short explanations",
},
ReasoningEffortPreset {
effort: ReasoningEffort::Medium,
description: "Provides a solid balance of reasoning depth and latency for general-purpose tasks".to_string(),
description:
"Provides a solid balance of reasoning depth and latency for general-purpose tasks",
},
ReasoningEffortPreset {
effort: ReasoningEffort::High,
description: "Maximizes reasoning depth for complex or ambiguous problems".to_string(),
},
],
supported_text_verbosity: ALL_TEXT_VERBOSITY,
is_default: false,
upgrade: Some(ModelUpgrade {
id: "gpt-5.1-codex-max".to_string(),
@@ -121,12 +162,12 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
}),
show_in_picker: true,
},
// Deprecated models.
// Deprecated GPT-5 variants kept for migrations / config compatibility.
ModelPreset {
id: "gpt-5-codex".to_string(),
model: "gpt-5-codex".to_string(),
display_name: "gpt-5-codex".to_string(),
description: "Optimized for codex.".to_string(),
id: "gpt-5-codex",
model: "gpt-5-codex",
display_name: "gpt-5-codex",
description: "Optimized for Code.",
default_reasoning_effort: ReasoningEffort::Medium,
supported_reasoning_efforts: vec![
ReasoningEffortPreset {
@@ -142,6 +183,7 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
description: "Maximizes reasoning depth for complex or ambiguous problems".to_string(),
},
],
supported_text_verbosity: ALL_TEXT_VERBOSITY,
is_default: false,
upgrade: Some(ModelUpgrade {
id: "gpt-5.1-codex-max".to_string(),
@@ -151,10 +193,10 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
show_in_picker: false,
},
ModelPreset {
id: "gpt-5-codex-mini".to_string(),
model: "gpt-5-codex-mini".to_string(),
display_name: "gpt-5-codex-mini".to_string(),
description: "Optimized for codex. Cheaper, faster, but less capable.".to_string(),
id: "gpt-5-codex-mini",
model: "gpt-5-codex-mini",
display_name: "gpt-5-codex-mini",
description: "Optimized for Code. Cheaper, faster, but less capable.",
default_reasoning_effort: ReasoningEffort::Medium,
supported_reasoning_efforts: vec![
ReasoningEffortPreset {
@@ -166,6 +208,7 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
description: "Maximizes reasoning depth for complex or ambiguous problems".to_string(),
},
],
supported_text_verbosity: ALL_TEXT_VERBOSITY,
is_default: false,
upgrade: Some(ModelUpgrade {
id: "gpt-5.1-codex-mini".to_string(),
@@ -187,17 +230,20 @@ static PRESETS: Lazy<Vec<ModelPreset>> = Lazy::new(|| {
},
ReasoningEffortPreset {
effort: ReasoningEffort::Low,
description: "Balances speed with some reasoning; useful for straightforward queries and short explanations".to_string(),
description:
"Balances speed with some reasoning; useful for straightforward queries and short explanations",
},
ReasoningEffortPreset {
effort: ReasoningEffort::Medium,
description: "Provides a solid balance of reasoning depth and latency for general-purpose tasks".to_string(),
description:
"Provides a solid balance of reasoning depth and latency for general-purpose tasks",
},
ReasoningEffortPreset {
effort: ReasoningEffort::High,
description: "Maximizes reasoning depth for complex or ambiguous problems".to_string(),
},
],
supported_text_verbosity: ALL_TEXT_VERBOSITY,
is_default: false,
upgrade: Some(ModelUpgrade {
id: "gpt-5.1-codex-max".to_string(),
@@ -222,13 +268,88 @@ pub fn all_model_presets() -> &'static Vec<ModelPreset> {
&PRESETS
}
fn find_preset_for_model(model: &str) -> Option<&'static ModelPreset> {
let model_lower = model.to_ascii_lowercase();
PRESETS.iter().find(|preset| {
preset.model.eq_ignore_ascii_case(&model_lower)
|| preset.id.eq_ignore_ascii_case(&model_lower)
|| preset.display_name.eq_ignore_ascii_case(&model_lower)
})
}
fn reasoning_effort_rank(effort: ReasoningEffort) -> u8 {
match effort {
ReasoningEffort::Minimal => 0,
ReasoningEffort::Low => 1,
ReasoningEffort::Medium => 2,
ReasoningEffort::High => 3,
ReasoningEffort::XHigh => 4,
}
}
pub fn clamp_reasoning_effort_for_model(
model: &str,
requested: ReasoningEffort,
) -> ReasoningEffort {
let Some(preset) = find_preset_for_model(model) else {
return requested;
};
if preset
.supported_reasoning_efforts
.iter()
.any(|opt| opt.effort == requested)
{
return requested;
}
let requested_rank = reasoning_effort_rank(requested);
preset
.supported_reasoning_efforts
.iter()
.min_by_key(|opt| {
let rank = reasoning_effort_rank(opt.effort);
(requested_rank.abs_diff(rank), u8::MAX - rank)
})
.map(|opt| opt.effort)
.unwrap_or(requested)
}
pub fn allowed_text_verbosity_for_model(model: &str) -> &'static [TextVerbosityConfig] {
find_preset_for_model(model)
.map(|preset| preset.supported_text_verbosity)
.unwrap_or(ALL_TEXT_VERBOSITY)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn only_one_default_model_is_configured() {
let default_models = PRESETS.iter().filter(|preset| preset.is_default).count();
assert!(default_models == 1);
assert_eq!(PRESETS.iter().filter(|preset| preset.is_default).count(), 1);
}
#[test]
fn gpt_5_1_codex_max_hidden_for_api_key_auth() {
let presets = builtin_model_presets(Some(AuthMode::ApiKey));
assert!(presets
.iter()
.all(|preset| preset.id != "gpt-5.1-codex-max"));
}
#[test]
fn clamp_reasoning_effort_downgrades_to_supported_level() {
let clamped = clamp_reasoning_effort_for_model(
"gpt-5.1-codex",
ReasoningEffort::XHigh,
);
assert_eq!(clamped, ReasoningEffort::High);
let clamped_minimal =
clamp_reasoning_effort_for_model("gpt-5.1-codex-mini", ReasoningEffort::Minimal);
assert_eq!(clamped_minimal, ReasoningEffort::Medium);
}
}
+8 -11
View File
@@ -646,7 +646,7 @@ metadata above):
- `codex.tool_decision`
- `tool_name`
- `call_id`
- `decision` (`approved`, `approved_for_session`, `denied`, or `abort`)
- `decision` (`approved`, `approved_execpolicy_amendment`, `approved_for_session`, `denied`, or `abort`)
- `source` (`config` or `user`)
- `codex.tool_result`
- `tool_name`
@@ -668,12 +668,12 @@ Set `otel.exporter` to control where events go:
endpoint, protocol, and headers your collector expects:
```toml
[otel]
exporter = { otlp-http = {
endpoint = "https://otel.example.com/v1/logs",
protocol = "binary",
headers = { "x-otlp-api-key" = "${OTLP_TOKEN}" }
}}
[otel.exporter."otlp-http"]
endpoint = "https://otel.example.com/v1/logs"
protocol = "binary"
[otel.exporter."otlp-http".headers]
"x-otlp-api-key" = "${OTLP_TOKEN}"
```
- `otlp-grpc` – streams OTLP log records over gRPC. Provide the endpoint and any
@@ -681,10 +681,7 @@ Set `otel.exporter` to control where events go:
```toml
[otel]
exporter = { otlp-grpc = {
endpoint = "https://otel.example.com:4317",
headers = { "x-otlp-meta" = "abc123" }
}}
exporter = { otlp-grpc = {endpoint = "https://otel.example.com:4317",headers = { "x-otlp-meta" = "abc123" }}}
```
Both OTLP exporters accept an optional `tls` block so you can trust a custom CA
+37 -1
View File
@@ -2,7 +2,19 @@
Every Code can enforce your own rules-based execution policy before it runs shell commands. Policies live in Starlark `.codexpolicy` files under `~/.code/policy` (Code still reads `~/.codex/policy` for backward compatibility).
## Create a policy
## How to create and edit rules
### TUI interactions
Codex CLI will present the option to whitelist commands when a command causes a prompt.
<img width="513" height="168" alt="Screenshot 2025-12-04 at 9 23 54 AM" src="https://github.com/user-attachments/assets/4c8ee8ea-3101-4a81-bb13-3f4a9aa02502" />
Whitelisted commands will no longer require your permission to run in current and subsequent sessions.
Under the hood, when you approve and whitelist a command, codex will edit `~/.codex/policy/default.execpolicy`.
### Editing `.execpolicy` files
1. Create a policy directory: `mkdir -p ~/.code/policy`.
2. Add one or more `.codexpolicy` files in that folder. Code automatically loads every `.codexpolicy` file in there on startup.
@@ -33,6 +45,30 @@ code execpolicy check --policy ~/.code/policy/default.codexpolicy git push origi
Pass multiple `--policy` flags to test how several files combine, and use `--pretty` for formatted JSON output. See the [`code-rs/execpolicy` README](../code-rs/execpolicy/README.md) for a more detailed walkthrough of the available syntax.
Example output when a rule matches:
```json
{
"matchedRules": [
{
"prefixRuleMatch": {
"matchedPrefix": ["git", "push"],
"decision": "prompt"
}
}
],
"decision": "prompt"
}
```
When no rules match, `matchedRules` is an empty array and `decision` is omitted.
```json
{
"matchedRules": []
}
```
## Status
`execpolicy` commands are still in preview. The API may have breaking changes in the future.
+62
View File
@@ -0,0 +1,62 @@
# Skills (experimental)
> **Warning:** This is an experimental and non-stable feature. If you depend on it, please expect breaking changes over the coming weeks and understand that there is currently no guarantee that this works well. Use at your own risk!
Codex can automatically discover reusable "skills" you keep on disk. A skill is a small bundle with a name, a short description (what it does and when to use it), and an optional body of instructions you can open when needed. Codex injects only the name, description, and file path into the runtime context; the body stays on disk.
## Where skills live
- Location (v1): `~/.codex/skills/**/SKILL.md` (recursive). Hidden entries and symlinks are skipped. Only files named exactly `SKILL.md` count.
- Sorting: rendered by name, then path for stability.
## File format
- YAML frontmatter + body.
- Required:
- `name` (non-empty, ≤100 chars, sanitized to one line)
- `description` (non-empty, ≤500 chars, sanitized to one line)
- Extra keys are ignored. The body can contain any Markdown; it is not injected into context.
## Loading and rendering
- Loaded once at startup.
- If valid skills exist, Codex appends a runtime-only `## Skills` section after `AGENTS.md`, one bullet per skill: `- <name>: <description> (file: /absolute/path/to/SKILL.md)`.
- If no valid skills exist, the section is omitted. On-disk files are never modified.
## Validation and errors
- Invalid skills (missing/invalid YAML, empty/over-length fields) trigger a blocking, dismissible startup modal in the TUI that lists each path and error. Errors are also logged. You can dismiss to continue (invalid skills are ignored) or exit. Fix SKILL.md files and restart to clear the modal.
## Create a skill
1. Create `~/.codex/skills/<skill-name>/`.
2. Add `SKILL.md`:
```
---
name: your-skill-name
description: what it does and when to use it (<=500 chars)
---
# Optional body
Add instructions, references, examples, or scripts (kept on disk).
```
3. Keep `name`/`description` within the limits; avoid newlines in those fields.
4. Restart Codex to load the new skill.
## Example
```
mkdir -p ~/.codex/skills/pdf-processing
cat <<'SKILL_EXAMPLE' > ~/.codex/skills/pdf-processing/SKILL.md
---
name: pdf-processing
description: Extract text and tables from PDFs; use when PDFs, forms, or document extraction are mentioned.
---
# PDF Processing
- Use pdfplumber to extract text.
- For form filling, see FORMS.md.
SKILL_EXAMPLE
```
+48
View File
@@ -0,0 +1,48 @@
set working-directory := "codex-rs"
set positional-arguments
# Display help
help:
just -l
# `codex`
alias c := codex
codex *args:
cargo run --bin codex -- "$@"
# `codex exec`
exec *args:
cargo run --bin codex -- exec "$@"
# `codex tui`
tui *args:
cargo run --bin codex -- tui "$@"
# Run the CLI version of the file-search crate.
file-search *args:
cargo run --bin code-file-search -- "$@"
# format code
fmt:
cargo fmt -- --config imports_granularity=Item
fix *args:
cargo clippy --fix --all-features --tests --allow-dirty "$@"
clippy:
cargo clippy --all-features --tests "$@"
install:
rustup show active-toolchain
cargo fetch
# Run `cargo nextest` since it's faster than `cargo test`, though including
# --no-fail-fast is important to ensure all tests are run.
#
# Run `cargo install cargo-nextest` if you don't have it installed.
test:
cargo nextest run --no-fail-fast
# Run the MCP server
mcp-server-run *args:
cargo run -p code-mcp-server -- "$@"
+1 -6
View File
@@ -8,14 +8,9 @@ import { resolveBashPath } from "./bashSelection";
import { readOsRelease } from "./osRelease";
import { resolveTargetTriple } from "./platform";
const scriptPath = process.argv[1]
? path.resolve(process.argv[1])
: process.cwd();
const __dirname = path.dirname(scriptPath);
async function main(): Promise<void> {
const targetTriple = resolveTargetTriple(process.platform, process.arch);
const vendorRoot = path.join(__dirname, "..", "vendor");
const vendorRoot = path.resolve(__dirname, "..", "vendor");
const targetRoot = path.join(vendorRoot, targetTriple);
const execveWrapperPath = path.join(targetRoot, "codex-execve-wrapper");
const serverPath = path.join(targetRoot, "codex-exec-mcp-server");
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018-Present Wez Furlong
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.