mirror of
https://github.com/openai/codex.git
synced 2026-06-02 11:22:01 +00:00
Add reasoning-only status surface item (#25504)
Closes #24886. ## Why Users can configure the TUI status line and terminal title with `model-with-reasoning`, but issue #24886 asks for a compact reasoning-only item. That lets a setup show just `default`, `low`, `medium`, `high`, or `xhigh` without repeating the model name. ## What changed - Added a `reasoning` item for `/statusline` and `/title` setup flows. - Rendered the item from the effective reasoning effort, including collaboration-mode overrides. - Registered `reasoning` with `codex doctor` so Codex-generated terminal-title config is not reported as invalid. - Updated TUI setup snapshots so the picker previews include the new item.
This commit is contained in:
@@ -143,6 +143,7 @@ fn terminal_title_item_id(item: &str) -> Option<&'static str> {
|
||||
"fast-mode" => Some("fast-mode"),
|
||||
"model" | "model-name" => Some("model"),
|
||||
"model-with-reasoning" => Some("model-with-reasoning"),
|
||||
"reasoning" => Some("reasoning"),
|
||||
"task-progress" => Some("task-progress"),
|
||||
_ => None,
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ expression: "render_lines(&view, 72)"
|
||||
[x] current-dir Current working directory
|
||||
[x] git-branch Current Git branch (omitted when unavaila…
|
||||
[ ] model-with-reasoning Current model name with reasoning level
|
||||
[ ] reasoning Current reasoning level
|
||||
[ ] project-name Project name (omitted when unavailable)
|
||||
[ ] pull-request-number Open pull request number for the current …
|
||||
|
||||
gpt-5-codex · ~/codex-rs · jif/statusline-preview
|
||||
Press space to toggle; ←/→ to move; enter to confirm and close; esc to
|
||||
|
||||
@@ -60,6 +60,9 @@ pub(crate) enum StatusLineItem {
|
||||
/// Model name with reasoning level suffix.
|
||||
ModelWithReasoning,
|
||||
|
||||
/// Current reasoning level.
|
||||
Reasoning,
|
||||
|
||||
/// Current working directory path.
|
||||
CurrentDir,
|
||||
|
||||
@@ -144,6 +147,7 @@ impl StatusLineItem {
|
||||
match self {
|
||||
StatusLineItem::ModelName => "Current model name",
|
||||
StatusLineItem::ModelWithReasoning => "Current model name with reasoning level",
|
||||
StatusLineItem::Reasoning => "Current reasoning level",
|
||||
StatusLineItem::CurrentDir => "Current working directory",
|
||||
StatusLineItem::ProjectRoot => "Project name (omitted when unavailable)",
|
||||
StatusLineItem::GitBranch => "Current Git branch (omitted when unavailable)",
|
||||
@@ -191,6 +195,7 @@ impl StatusLineItem {
|
||||
match self {
|
||||
StatusLineItem::ModelName => StatusSurfacePreviewItem::Model,
|
||||
StatusLineItem::ModelWithReasoning => StatusSurfacePreviewItem::ModelWithReasoning,
|
||||
StatusLineItem::Reasoning => StatusSurfacePreviewItem::Reasoning,
|
||||
StatusLineItem::CurrentDir => StatusSurfacePreviewItem::CurrentDir,
|
||||
StatusLineItem::ProjectRoot => StatusSurfacePreviewItem::ProjectRoot,
|
||||
StatusLineItem::GitBranch => StatusSurfacePreviewItem::GitBranch,
|
||||
@@ -450,6 +455,15 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reasoning_is_selectable_id() {
|
||||
assert_eq!(StatusLineItem::Reasoning.to_string(), "reasoning");
|
||||
assert_eq!(
|
||||
"reasoning".parse::<StatusLineItem>(),
|
||||
Ok(StatusLineItem::Reasoning)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_state_is_canonical_and_accepts_status_legacy_id() {
|
||||
assert_eq!(StatusLineItem::Status.to_string(), "run-state");
|
||||
|
||||
@@ -30,7 +30,9 @@ enum StatusLineAccent {
|
||||
impl StatusLineAccent {
|
||||
fn for_item(item: StatusLineItem) -> Self {
|
||||
match item {
|
||||
StatusLineItem::ModelName | StatusLineItem::ModelWithReasoning => Self::Model,
|
||||
StatusLineItem::ModelName
|
||||
| StatusLineItem::ModelWithReasoning
|
||||
| StatusLineItem::Reasoning => Self::Model,
|
||||
StatusLineItem::CurrentDir | StatusLineItem::ProjectRoot => Self::Path,
|
||||
StatusLineItem::GitBranch
|
||||
| StatusLineItem::PullRequestNumber
|
||||
|
||||
@@ -32,6 +32,7 @@ pub(crate) enum StatusSurfacePreviewItem {
|
||||
RawOutput,
|
||||
Model,
|
||||
ModelWithReasoning,
|
||||
Reasoning,
|
||||
TaskProgress,
|
||||
}
|
||||
|
||||
@@ -63,6 +64,7 @@ impl StatusSurfacePreviewItem {
|
||||
StatusSurfacePreviewItem::RawOutput => "raw output",
|
||||
StatusSurfacePreviewItem::Model => "gpt-5.2-codex",
|
||||
StatusSurfacePreviewItem::ModelWithReasoning => "gpt-5.2-codex medium",
|
||||
StatusSurfacePreviewItem::Reasoning => "medium",
|
||||
StatusSurfacePreviewItem::TaskProgress => "Tasks 0/0",
|
||||
}
|
||||
}
|
||||
@@ -94,6 +96,7 @@ impl StatusSurfacePreviewItem {
|
||||
Self::RawOutput,
|
||||
Self::Model,
|
||||
Self::ModelWithReasoning,
|
||||
Self::Reasoning,
|
||||
Self::TaskProgress,
|
||||
]
|
||||
.into_iter()
|
||||
|
||||
@@ -82,6 +82,8 @@ pub(crate) enum TerminalTitleItem {
|
||||
Model,
|
||||
/// Current model name with reasoning level.
|
||||
ModelWithReasoning,
|
||||
/// Current reasoning level.
|
||||
Reasoning,
|
||||
/// Latest checklist task progress from `update_plan` (if available).
|
||||
TaskProgress,
|
||||
}
|
||||
@@ -122,6 +124,7 @@ impl TerminalTitleItem {
|
||||
TerminalTitleItem::FastMode => "Whether Fast mode is currently active",
|
||||
TerminalTitleItem::Model => "Current model name",
|
||||
TerminalTitleItem::ModelWithReasoning => "Current model name with reasoning level",
|
||||
TerminalTitleItem::Reasoning => "Current reasoning level",
|
||||
TerminalTitleItem::TaskProgress => {
|
||||
"Latest task progress from update_plan (omitted until available)"
|
||||
}
|
||||
@@ -153,6 +156,7 @@ impl TerminalTitleItem {
|
||||
TerminalTitleItem::ModelWithReasoning => {
|
||||
Some(StatusSurfacePreviewItem::ModelWithReasoning)
|
||||
}
|
||||
TerminalTitleItem::Reasoning => Some(StatusSurfacePreviewItem::Reasoning),
|
||||
TerminalTitleItem::TaskProgress => Some(StatusSurfacePreviewItem::TaskProgress),
|
||||
}
|
||||
}
|
||||
@@ -516,6 +520,15 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reasoning_is_selectable_id() {
|
||||
assert_eq!(TerminalTitleItem::Reasoning.to_string(), "reasoning");
|
||||
assert_eq!(
|
||||
"reasoning".parse::<TerminalTitleItem>(),
|
||||
Ok(TerminalTitleItem::Reasoning)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_terminal_title_items_accepts_kebab_case_variants() {
|
||||
let items = parse_terminal_title_items(
|
||||
@@ -530,6 +543,7 @@ mod tests {
|
||||
"project-name",
|
||||
"model",
|
||||
"model-with-reasoning",
|
||||
"reasoning",
|
||||
"weekly-limit",
|
||||
"codex-version",
|
||||
"used-tokens",
|
||||
@@ -553,6 +567,7 @@ mod tests {
|
||||
TerminalTitleItem::Project,
|
||||
TerminalTitleItem::Model,
|
||||
TerminalTitleItem::ModelWithReasoning,
|
||||
TerminalTitleItem::Reasoning,
|
||||
TerminalTitleItem::WeeklyLimit,
|
||||
TerminalTitleItem::CodexVersion,
|
||||
TerminalTitleItem::UsedTokens,
|
||||
|
||||
@@ -14,7 +14,7 @@ expression: status_line_popup_snapshot(&mut chat)
|
||||
[x] thread-title Current thread title, or thread identifier when unnamed
|
||||
[ ] model Current model name
|
||||
[ ] model-with-reasoning Current model name with reasoning level
|
||||
[ ] current-dir Current working directory
|
||||
[ ] reasoning Current reasoning level
|
||||
|
||||
my-project · feat/awesome-feature · thread title
|
||||
Press space to toggle; ←/→ to move; enter to confirm and close; esc to close
|
||||
|
||||
@@ -14,7 +14,7 @@ expression: status_line_popup_snapshot(&mut chat)
|
||||
[x] thread-title Current thread title, or thread identifier when unnamed
|
||||
[ ] model Current model name
|
||||
[ ] model-with-reasoning Current model name with reasoning level
|
||||
[ ] current-dir Current working directory
|
||||
[ ] reasoning Current reasoning level
|
||||
|
||||
preview-live-root · feature/live-preview-branch · Live preview thread
|
||||
Press space to toggle; ←/→ to move; enter to confirm and close; esc to close
|
||||
|
||||
@@ -14,7 +14,7 @@ expression: status_line_popup_snapshot(&mut chat)
|
||||
[x] thread-title Current thread title, or thread identifier when unnamed
|
||||
[ ] model Current model name
|
||||
[ ] model-with-reasoning Current model name with reasoning level
|
||||
[ ] current-dir Current working directory
|
||||
[ ] reasoning Current reasoning level
|
||||
|
||||
my-project · feature/mixed-preview · Mixed preview thread
|
||||
Press space to toggle; ←/→ to move; enter to confirm and close; esc to close
|
||||
|
||||
@@ -13,8 +13,8 @@ expression: status_line_popup_snapshot(&mut chat)
|
||||
[x] weekly-limit Remaining usage on the weekly usage limit (omitted when unavailable)
|
||||
[ ] model Current model name
|
||||
[ ] model-with-reasoning Current model name with reasoning level
|
||||
[ ] reasoning Current reasoning level
|
||||
[ ] current-dir Current working directory
|
||||
[ ] project-name Project name (omitted when unavailable)
|
||||
|
||||
monthly 65% left · weekly 50% left
|
||||
Press space to toggle; ←/→ to move; enter to confirm and close; esc to close
|
||||
|
||||
@@ -562,6 +562,7 @@ impl ChatWidget {
|
||||
match item {
|
||||
StatusLineItem::ModelName => Some(self.model_display_name().to_string()),
|
||||
StatusLineItem::ModelWithReasoning => Some(self.model_with_reasoning_display_name()),
|
||||
StatusLineItem::Reasoning => Some(self.reasoning_display_name().to_string()),
|
||||
StatusLineItem::CurrentDir => {
|
||||
Some(format_directory_display(
|
||||
self.status_line_cwd(),
|
||||
@@ -694,6 +695,7 @@ impl ChatWidget {
|
||||
StatusSurfacePreviewItem::RawOutput => StatusLineItem::RawOutput,
|
||||
StatusSurfacePreviewItem::Model => StatusLineItem::ModelName,
|
||||
StatusSurfacePreviewItem::ModelWithReasoning => StatusLineItem::ModelWithReasoning,
|
||||
StatusSurfacePreviewItem::Reasoning => StatusLineItem::Reasoning,
|
||||
};
|
||||
self.status_line_value_for_item(status_line_item)
|
||||
}
|
||||
@@ -759,12 +761,20 @@ impl ChatWidget {
|
||||
self.model_with_reasoning_display_name(),
|
||||
/*max_chars*/ 32,
|
||||
)),
|
||||
TerminalTitleItem::Reasoning => Some(Self::truncate_terminal_title_part(
|
||||
self.reasoning_display_name().to_string(),
|
||||
/*max_chars*/ 32,
|
||||
)),
|
||||
TerminalTitleItem::TaskProgress => self.terminal_title_task_progress(),
|
||||
}
|
||||
}
|
||||
|
||||
fn reasoning_display_name(&self) -> &'static str {
|
||||
Self::status_line_reasoning_effort_label(self.effective_reasoning_effort())
|
||||
}
|
||||
|
||||
fn model_with_reasoning_display_name(&self) -> String {
|
||||
let label = Self::status_line_reasoning_effort_label(self.effective_reasoning_effort());
|
||||
let label = self.reasoning_display_name();
|
||||
let service_tier_label = self
|
||||
.current_service_tier()
|
||||
.and_then(|service_tier| {
|
||||
|
||||
@@ -2208,6 +2208,37 @@ async fn terminal_title_model_updates_on_model_change_without_manual_refresh() {
|
||||
assert_eq!(chat.last_terminal_title, Some("gpt-5.3-codex".to_string()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn status_line_and_terminal_title_reasoning_render_only_effort() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.config.tui_status_line = Some(vec!["reasoning".to_string()]);
|
||||
chat.config.tui_terminal_title = Some(vec!["reasoning".to_string()]);
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::XHigh));
|
||||
chat.set_service_tier(Some(ServiceTier::Fast.request_value().to_string()));
|
||||
|
||||
chat.refresh_status_line();
|
||||
chat.refresh_terminal_title();
|
||||
|
||||
assert_eq!(status_line_text(&chat), Some("xhigh".to_string()));
|
||||
assert_eq!(chat.last_terminal_title, Some("xhigh".to_string()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn status_line_reasoning_updates_on_mode_switch_without_manual_refresh() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.3-codex")).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, /*enabled*/ true);
|
||||
chat.config.tui_status_line = Some(vec!["reasoning".to_string()]);
|
||||
chat.set_reasoning_effort(Some(ReasoningEffortConfig::High));
|
||||
|
||||
assert_eq!(status_line_text(&chat), Some("high".to_string()));
|
||||
|
||||
let plan_mask = collaboration_modes::plan_mask(chat.model_catalog.as_ref())
|
||||
.expect("expected plan collaboration mode");
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
|
||||
assert_eq!(status_line_text(&chat), Some("medium".to_string()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn status_line_model_with_reasoning_updates_on_mode_switch_without_manual_refresh() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.3-codex")).await;
|
||||
|
||||
Reference in New Issue
Block a user