Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions codex-rs/core/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@
"include_apply_patch_tool": {
"type": "boolean"
},
"personality": {
"type": "boolean"
},
"powershell_utf8": {
"type": "boolean"
},
Expand Down Expand Up @@ -1181,6 +1184,9 @@
"include_apply_patch_tool": {
"type": "boolean"
},
"personality": {
"type": "boolean"
},
"powershell_utf8": {
"type": "boolean"
},
Expand Down
12 changes: 12 additions & 0 deletions codex-rs/core/src/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ pub enum Feature {
Steer,
/// Enable collaboration modes (Plan, Code, Pair Programming, Execute).
CollaborationModes,
/// Enable personality selection in the TUI.
Personality,
/// Use the Responses API WebSocket transport for OpenAI by default.
ResponsesWebsockets,
}
Expand Down Expand Up @@ -543,6 +545,16 @@ pub const FEATURES: &[FeatureSpec] = &[
stage: Stage::UnderDevelopment,
default_enabled: false,
},
FeatureSpec {
id: Feature::Personality,
key: "personality",
stage: Stage::Experimental {
name: "Personality",
menu_description: "Choose a communication style for Codex.",
announcement: "NEW! Update codex's communication style with /personality. Enable in /experimental!",
},
default_enabled: false,
},
FeatureSpec {
id: Feature::ResponsesWebsockets,
key: "responses_websockets",
Expand Down
10 changes: 10 additions & 0 deletions codex-rs/core/src/models_manager/model_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,16 @@ pub(crate) fn find_model_info_for_slug(slug: &str) -> ModelInfo {
truncation_policy: TruncationPolicyConfig::tokens(10_000),
context_window: Some(CONTEXT_WINDOW_272K),
supported_reasoning_levels: supported_reasoning_level_low_medium_high_xhigh(),
model_instructions_template: Some(ModelInstructionsTemplate {
template: GPT_5_2_CODEX_INSTRUCTIONS_TEMPLATE.to_string(),
personality_messages: Some(PersonalityMessages(BTreeMap::from([(
Personality::Friendly,
PERSONALITY_FRIENDLY.to_string(),
), (
Personality::Pragmatic,
PERSONALITY_PRAGMATIC.to_string(),
)]))),
}),
)
} else if slug.starts_with("gpt-5.1-codex-max") {
model_info!(
Expand Down
32 changes: 18 additions & 14 deletions codex-rs/tui/src/chatwidget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2892,6 +2892,7 @@ impl ChatWidget {
let personality = self
.config
.model_personality
.filter(|_| self.config.features.enabled(Feature::Personality))
.filter(|_| self.current_model_supports_personality());
let op = Op::UserTurn {
items,
Expand Down Expand Up @@ -3455,19 +3456,23 @@ impl ChatWidget {
);
return;
}
if !self.current_model_supports_personality() {
let current_model = self.current_model();
self.add_error_message(format!(
"Current model ({current_model}) doesn't support personalities. Try /model to pick a different model."
));
return;
}
self.open_personality_popup_for_current_model();
}

fn open_personality_popup_for_current_model(&mut self) {
let current_model = self.current_model();
let current_personality = self.config.model_personality;
let current_personality = self
.config
.model_personality
.unwrap_or(Personality::Friendly);
let personalities = [Personality::Friendly, Personality::Pragmatic];
let supports_personality = self.current_model_supports_personality();
let disabled_message = (!supports_personality).then(|| {
format!(
"Current model ({current_model}) doesn't support personalities. Try /model to switch to a newer model."
)
});

let items: Vec<SelectionItem> = personalities
.into_iter()
Expand All @@ -3492,7 +3497,7 @@ impl ChatWidget {
SelectionItem {
name,
description,
is_current: current_personality == Some(personality),
is_current: current_personality == personality,
is_disabled: !supports_personality,
actions,
dismiss_on_select: true,
Expand All @@ -3504,11 +3509,8 @@ impl ChatWidget {
let mut header = ColumnRenderable::new();
header.push(Line::from("Select Personality".bold()));
header.push(Line::from(
"Choose a communication style for future responses.".dim(),
"Choose a communication style for Codex. Disable in /experimental.".dim(),
));
if let Some(message) = disabled_message {
header.push(Line::from(message.red()));
}

self.bottom_pane.show_selection_view(SelectionViewParams {
header: Box::new(header),
Expand Down Expand Up @@ -4714,6 +4716,9 @@ impl ChatWidget {
self.refresh_model_display();
self.request_redraw();
}
if feature == Feature::Personality {
self.sync_personality_command_enabled();
}
#[cfg(target_os = "windows")]
if matches!(
feature,
Expand Down Expand Up @@ -4780,7 +4785,6 @@ impl ChatWidget {
mask.model = Some(model.to_string());
}
self.refresh_model_display();
self.sync_personality_command_enabled();
}

pub(crate) fn current_model(&self) -> &str {
Expand All @@ -4795,7 +4799,7 @@ impl ChatWidget {

fn sync_personality_command_enabled(&mut self) {
self.bottom_pane
.set_personality_command_enabled(self.current_model_supports_personality());
.set_personality_command_enabled(self.config.features.enabled(Feature::Personality));
}

fn current_model_supports_personality(&self) -> bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ source: tui/src/chatwidget/tests.rs
expression: popup
---
Select Personality
Choose a communication style for future responses.
Choose a communication style for Codex. Disable in /experimental.

› 1. Friendly Warm, collaborative, and helpful.
2. Pragmatic Concise, task-focused, and direct.
› 1. Friendly (current) Warm, collaborative, and helpful.
2. Pragmatic Concise, task-focused, and direct.

Press enter to confirm or esc to go back
1 change: 1 addition & 0 deletions codex-rs/tui/src/chatwidget/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2408,6 +2408,7 @@ async fn collab_mode_enabling_keeps_custom_until_selected() {
#[tokio::test]
async fn user_turn_includes_personality_from_config() {
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(Some("bengalfox")).await;
chat.set_feature_enabled(Feature::Personality, true);
chat.thread_id = Some(ThreadId::new());
chat.set_model("bengalfox");
chat.set_personality(Personality::Friendly);
Expand Down
2 changes: 1 addition & 1 deletion codex-rs/tui/src/slash_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ impl SlashCommand {
SlashCommand::Status => "show current session configuration and token usage",
SlashCommand::Ps => "list background terminals",
SlashCommand::Model => "choose what model and reasoning effort to use",
SlashCommand::Personality => "choose a communication style for responses",
SlashCommand::Personality => "choose a communication style for Codex",
SlashCommand::Collab => "change collaboration mode (experimental)",
SlashCommand::Agent => "switch the active agent thread",
SlashCommand::Approvals => "choose what Codex can do without approval",
Expand Down
Loading