Skip to content

Comments

feat(tui): add /title terminal title configuration#12334

Open
yvolovich-cyber wants to merge 13 commits intomainfrom
codex/title-command
Open

feat(tui): add /title terminal title configuration#12334
yvolovich-cyber wants to merge 13 commits intomainfrom
codex/title-command

Conversation

@yvolovich-cyber
Copy link
Contributor

@yvolovich-cyber yvolovich-cyber commented Feb 20, 2026

Why

We want a first class way to make terminal tabs/windows distinguishable when multiple Codex sessions are open.

This adds /title as a separate command from /statusline, with secure terminal title rendering and a configurable title item order.

What changed

image
  • Added /title slash command and terminal title picker UI.
  • Added persisted config for tui.terminal_title with default order project | status.
  • Added terminal title items: project, status, thread, git-branch, model, task-progress.
  • Added dynamic title rendering that updates from runtime state.
  • Added terminal title sanitization before writing OSC 0.
  • Added live /title preview while the picker is open, and rollback on Esc/cancel.
  • Updated the compact background-terminal status label in the title to Waiting....
  • Fixed a thread/resume race in app-server so a running thread resume response reports Active consistently when an active turn snapshot exists (this was the CI failure on Linux Bazel jobs).

Security

The terminal title path sanitizes untrusted text before writing OSC 0:

  • strips control characters (C0, DEL, and C1)
  • strips bidi and other non-rendering formatting characters that can mislead users
  • normalizes whitespace
  • caps title length
  • uses the canonical OSC string terminator (ST, ESC \\)

References used while implementing this:

Verification

Ran:

  • cargo test -p codex-tui
  • cargo test -p codex-app-server --test all thread_resume_rejoins_running_thread_even_with_override_mismatch -- --nocapture
  • cargo test -p codex-core (earlier during feature work; local core/tests/all still had unrelated failures in this checkout)
  • cargo check -p codex-tui
  • just fix -p codex-tui
  • just fix -p codex-app-server
  • just fmt

Did not run:

  • cargo test --all-features (shared crates changed earlier in the feature; asking before running the full workspace suite)

@yvolovich-cyber yvolovich-cyber changed the title Add /title command for secure terminal title configuration feat(tui): add /title terminal title configuration Feb 20, 2026
@yvolovich-cyber
Copy link
Contributor Author

@codex review

@yvolovich-cyber
Copy link
Contributor Author

@codex review

@yvolovich-cyber
Copy link
Contributor Author

@codex review security in codex-rs/tui/src/terminal_title.rs

@chatgpt-codex-connector
Copy link
Contributor

Codex Review: Didn't find any major issues. Bravo.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a first-class /title configuration flow to the TUI so terminal tabs/windows can be distinguished across concurrent Codex sessions, including secure OSC 0 title rendering and persisted ordering of title “items”. Also includes an app-server fix to make running-thread resume status reporting consistent.

Changes:

  • Introduces terminal-title OSC writer with sanitization + tests, and wires dynamic title refresh into ChatWidget.
  • Adds /title slash command + bottom-pane picker UI (with live preview + rollback on cancel) and persists config via tui.terminal_title.
  • Fixes a thread resume race in app-server so a running thread reliably reports Active when an active turn snapshot exists.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
codex-rs/tui/src/terminal_title.rs Implements OSC 0 title writing with sanitization and unit tests.
codex-rs/tui/src/slash_command.rs Adds /title to the slash-command set and help text.
codex-rs/tui/src/lib.rs Registers the new terminal_title module.
codex-rs/tui/src/chatwidget.rs Adds terminal title config/defaults, live rendering, preview/rollback mechanics, and refresh hooks.
codex-rs/tui/src/chatwidget/tests.rs Adds tests for preview rollback and status text mapping for title rendering.
codex-rs/tui/src/bottom_pane/title_setup.rs Adds the terminal title picker UI (multi-select + ordering + live preview events).
codex-rs/tui/src/bottom_pane/mod.rs Exposes terminal title picker view + item enum.
codex-rs/tui/src/bottom_pane/snapshots/...terminal_title_setup_basic.snap Adds snapshot coverage for the new picker UI.
codex-rs/tui/src/app_event.rs Adds events for terminal title setup/preview/cancel.
codex-rs/tui/src/app.rs Handles new events and persists tui.terminal_title edits.
codex-rs/core/src/config/types.rs Adds tui.terminal_title to typed config.
codex-rs/core/src/config/mod.rs Plumbs config field into runtime Config and defaults/tests.
codex-rs/core/src/config/edit.rs Adds a config edit helper to persist terminal title items.
codex-rs/core/config.schema.json Adds schema documentation for tui.terminal_title.
codex-rs/app-server/src/codex_message_processor.rs Fixes resume-status race by preferring active turn snapshot over idle watch status.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@yvolovich-cyber
Copy link
Contributor Author

@codex review

Copy link
Contributor

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

let (items, _) = self.status_line_items_with_invalids();
if items.is_empty() || !items.contains(&StatusLineItem::GitBranch) {
return;

P2 Badge Refresh git-branch title when only /title enables it

Update request_status_line_branch_refresh to consider terminal-title config too. It currently returns unless the status line includes git-branch, but terminal titles now also use the same cached branch value. If a user enables git-branch in /title only, branch lookup is never re-triggered after turn completion, so the title can show a stale branch after git checkout/branch-changing commands.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@etraut-openai etraut-openai added the oai PRs contributed by OpenAI employees label Feb 20, 2026
Copy link
Contributor Author

@yvolovich-cyber yvolovich-cyber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

let (items, _) = self.status_line_items_with_invalids();
if items.is_empty() || !items.contains(&StatusLineItem::GitBranch) {
return;

P2 Badge Refresh git-branch title when only /title enables it
Update request_status_line_branch_refresh to consider terminal-title config too. It currently returns unless the status line includes git-branch, but terminal titles now also use the same cached branch value. If a user enables git-branch in /title only, branch lookup is never re-triggered after turn completion, so the title can show a stale branch after git checkout/branch-changing commands.

Addressed it in 2cf7abd05.

What changed:

  • request_status_line_branch_refresh() now refreshes when git-branch is configured in either /statusline or /title
  • added a regression test for the title-only git-branch case after TurnComplete

@yvolovich-cyber
Copy link
Contributor Author

@codex review

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2cf7abd055

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@yvolovich-cyber yvolovich-cyber force-pushed the codex/title-command branch 2 times, most recently from 800268f to 9f62b39 Compare February 23, 2026 11:54
@fcoury
Copy link
Contributor

fcoury commented Feb 23, 2026

@yvolovich-cyber really great to see the MultiSelectPicker being used in the wild.

I just took this branch for a test drive and it worked beautifully with tmux. I use a configuration where I explicitly show the pane titles and it's so much easier to spot what's happening with this feature in place:

image

Specially for someone who has ten thousand codex worktrees like myself, and I'm pretty sure I'm not the only one 😄

I also did a pass on the code changes and they look good to me 👍

Copy link
Collaborator

@jif-oai jif-oai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see anything that reset the title after a CMD + C. Can you make sure the title always get reset when we exit Codex?

@yvolovich-cyber
Copy link
Contributor Author

I don't see anything that reset the title after a CMD + C. Can you make sure the title always get reset when we exit Codex?

Good catch. I added best effort terminal-title cleanup in ChatWidget::drop, so when the TUI exits (including Ctrl+C or Ctrl+D quit paths and shutdown-complete exit), it clears the terminal title if one was set. clear_terminal_title() is already TTY-gated and failures stay debug-only. Included in be6d8f3bc.

yvolovich-cyber added a commit that referenced this pull request Feb 23, 2026
yvolovich-cyber added a commit that referenced this pull request Feb 23, 2026
@jif-oai
Copy link
Collaborator

jif-oai commented Feb 24, 2026

@codex review

@chatgpt-codex-connector
Copy link
Contributor

Codex Review: Didn't find any major issues. Breezy!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return Ok(());
}

execute!(stdout(), SetWindowTitle(String::new()))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not the same thing as restoring the previous one

Copy link
Contributor Author

@yvolovich-cyber yvolovich-cyber Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no portable and reliable way to restore (specifically read) terminal title.
We only clear the title Codex set, not to restore whatever title was there before, which keeps the behavior portable across terminals.
I also changed set_terminal_title() so "sanitized to no visible content" is no longer treated as an implicit clear_terminal_title().

fn set_status(&mut self, header: String, details: Option<String>) {
self.current_status_header = header.clone();
self.bottom_pane.update_status(header, details);
self.refresh_terminal_title();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in the end this should become async. Can be done as follow-up

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I kept this synchronous for now, but I narrowed the hot path so set_status() only refreshes the terminal title when /title actually includes status, and the shared status-surface path now reuses cached/shared state instead of recomputing it. I think making the remaining title refresh path async can be a follow-up.

.iter()
.map(|id| id.parse::<TerminalTitleItem>())
.collect::<Result<Vec<_>, _>>()
.unwrap_or_default();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't call app_event.send() if the parsing fail

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. on_change() and on_confirm() now return early on parse failure instead of sending an app event. I also factored the parsing into parse_terminal_title_items() so preview, change, and confirm all share the same validation path.

self.warn_invalid_terminal_title_items_once(&selections.invalid_terminal_title_items);
self.sync_status_surface_shared_state(&selections);
self.refresh_status_line_from_selections(&selections);
self.refresh_terminal_title_from_selections(&selections);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strange to update the title in a function named "..._status_line"

Copy link
Contributor Author

@yvolovich-cyber yvolovich-cyber Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed the shared entrypoint to refresh_status_surfaces() so the name matches what it does. refresh_status_line_from_selections() stays as the footer-only renderer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

oai PRs contributed by OpenAI employees

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants