feat(tui/resume): show session resume hint on exit

This commit is contained in:
James Peter
2025-09-27 07:28:22 +10:00
parent eed19520e7
commit 13dd4ca0db
5 changed files with 80 additions and 11 deletions
+35 -4
View File
@@ -22,6 +22,8 @@ use codex_core::find_conversation_path_by_id_str;
use codex_core::RolloutRecorder;
use codex_exec::Cli as ExecCli;
use codex_tui::Cli as TuiCli;
use codex_tui::ExitSummary;
use codex_tui::RESUME_COMMAND_NAME;
use std::path::Path;
use std::path::PathBuf;
use std::process;
@@ -257,9 +259,22 @@ async fn cli_main(codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()
&mut interactive.config_overrides,
root_config_overrides.clone(),
);
let usage = codex_tui::run_main(interactive, codex_linux_sandbox_exe).await?;
if !usage.is_zero() {
println!("{}", codex_core::protocol::FinalOutput::from(usage));
let ExitSummary {
token_usage,
session_id,
} = codex_tui::run_main(interactive, codex_linux_sandbox_exe).await?;
if !token_usage.is_zero() {
println!(
"{}",
codex_core::protocol::FinalOutput::from(token_usage.clone())
);
}
if let Some(session_id) = session_id {
println!(
"To continue this session, run {} resume {}.",
RESUME_COMMAND_NAME,
session_id
);
}
}
Some(Subcommand::Exec(mut exec_cli)) => {
@@ -287,7 +302,23 @@ async fn cli_main(codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()
last,
config_overrides,
);
codex_tui::run_main(interactive, codex_linux_sandbox_exe).await?;
let ExitSummary {
token_usage,
session_id,
} = codex_tui::run_main(interactive, codex_linux_sandbox_exe).await?;
if !token_usage.is_zero() {
println!(
"{}",
codex_core::protocol::FinalOutput::from(token_usage.clone())
);
}
if let Some(session_id) = session_id {
println!(
"To continue this session, run {} resume {}.",
RESUME_COMMAND_NAME,
session_id
);
}
}
Some(Subcommand::Login(mut login_cli)) => {
prepend_config_flags(
+7
View File
@@ -2478,6 +2478,13 @@ impl App<'_> {
usage
}
pub(crate) fn session_id(&self) -> Option<uuid::Uuid> {
match &self.app_state {
AppState::Chat { widget } => widget.session_id(),
AppState::Onboarding { .. } => None,
}
}
/// Return a human-readable performance summary if timing was enabled.
pub(crate) fn perf_summary(&self) -> Option<String> {
if !self.timing_enabled {
+4
View File
@@ -13549,6 +13549,10 @@ impl ChatWidget<'_> {
&self.total_token_usage
}
pub(crate) fn session_id(&self) -> Option<uuid::Uuid> {
self.session_id
}
pub(crate) fn clear_token_usage(&mut self) {
self.total_token_usage = TokenUsage::default();
self.rate_limit_snapshot = None;
+16 -3
View File
@@ -27,6 +27,7 @@ use std::path::PathBuf;
use tracing_appender::non_blocking;
use tracing_subscriber::EnvFilter;
use tracing_subscriber::prelude::*;
use uuid::Uuid;
mod app;
mod app_event;
@@ -112,10 +113,18 @@ fn theme_configured_in_config_file(codex_home: &std::path::Path) -> bool {
// (tests access modules directly within the crate)
#[derive(Debug)]
pub struct ExitSummary {
pub token_usage: codex_core::protocol::TokenUsage,
pub session_id: Option<Uuid>,
}
pub const RESUME_COMMAND_NAME: &str = "code";
pub async fn run_main(
mut cli: Cli,
codex_linux_sandbox_exe: Option<PathBuf>,
) -> std::io::Result<codex_core::protocol::TokenUsage> {
) -> std::io::Result<ExitSummary> {
cli.finalize_defaults();
let (sandbox_mode, approval_policy) = if cli.full_auto {
@@ -307,7 +316,7 @@ fn run_ratatui_app(
startup_footer_notice: Option<String>,
latest_upgrade_version: Option<String>,
theme_configured_explicitly: bool,
) -> color_eyre::Result<codex_core::protocol::TokenUsage> {
) -> color_eyre::Result<ExitSummary> {
color_eyre::install()?;
// Forward panic reports through tracing so they appear in the UI status
@@ -365,6 +374,7 @@ fn run_ratatui_app(
);
let app_result = app.run(&mut terminal);
let session_id = app.session_id();
let usage = app.token_usage();
// Optionally print timing summary to stderr after restoring the terminal.
@@ -381,7 +391,10 @@ fn run_ratatui_app(
}
// ignore error when collecting usage – report underlying error instead
app_result.map(|_| usage)
app_result.map(|_| ExitSummary {
token_usage: usage,
session_id,
})
}
#[expect(
+18 -4
View File
@@ -2,7 +2,9 @@ use clap::Parser;
use codex_arg0::arg0_dispatch_or_else;
use codex_common::CliConfigOverrides;
use codex_tui::Cli;
use codex_tui::ExitSummary;
use codex_tui::run_main;
use codex_tui::RESUME_COMMAND_NAME;
#[derive(Parser, Debug)]
struct TopCli {
@@ -22,10 +24,22 @@ fn main() -> anyhow::Result<()> {
.config_overrides
.raw_overrides
.splice(0..0, top_cli.config_overrides.raw_overrides);
let usage = run_main(inner, codex_linux_sandbox_exe).await?;
if !usage.is_zero() {
println!("{}", codex_core::protocol::FinalOutput::from(usage));
// Conversation id hint not available in fork run_main() return type.
let ExitSummary {
token_usage,
session_id,
} = run_main(inner, codex_linux_sandbox_exe).await?;
if !token_usage.is_zero() {
println!(
"{}",
codex_core::protocol::FinalOutput::from(token_usage.clone())
);
}
if let Some(session_id) = session_id {
println!(
"To continue this session, run {} resume {}.",
RESUME_COMMAND_NAME,
session_id
);
}
Ok(())
})