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:
Eric Traut authored and GitHub committed 2026-06-01 09:30:20 -07:00
1 parent 6681446477
commit f1d029cf75
12 files changed
+83 -7

No files matched your search

@@ -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
+11 -1
View File
@@ -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;