From 6119cb2b32587b31d37a9d6d2de6aafcb6c75810 Mon Sep 17 00:00:00 2001 From: pap Date: Mon, 5 Jan 2026 21:23:07 +0000 Subject: [PATCH] adding session naming with /rename and metadata storage in SessionMeta --- codex-rs/core/src/codex.rs | 45 +++++++++++++++++++++++++++++++++ codex-rs/tui/src/chatwidget.rs | 17 +++++++++++++ codex-rs/tui2/src/chatwidget.rs | 17 +++++++++++++ 3 files changed, 79 insertions(+) diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index d374b08aaa..ae249b4399 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -1699,6 +1699,9 @@ async fn submission_loop(sess: Arc, config: Arc, rx_sub: Receiv Op::ThreadRollback { num_turns } => { handlers::thread_rollback(&sess, sub.id.clone(), num_turns).await; } + Op::SetSessionTitle { title } => { + handlers::set_session_title(&sess, sub.id.clone(), title).await; + } Op::RunUserShellCommand { command } => { handlers::run_user_shell_command( &sess, @@ -2122,6 +2125,48 @@ mod handlers { .await; } + pub async fn set_session_title(sess: &Arc, sub_id: String, title: String) { + let title = title.trim().to_string(); + if title.is_empty() { + let event = Event { + id: sub_id, + msg: EventMsg::Error(ErrorEvent { + message: "Session title cannot be empty.".to_string(), + codex_error_info: Some(CodexErrorInfo::BadRequest), + }), + }; + sess.send_event_raw(event).await; + return; + } + + let recorder = { + let guard = sess.services.rollout.lock().await; + guard.clone() + }; + let Some(recorder) = recorder else { + let event = Event { + id: sub_id, + msg: EventMsg::Error(ErrorEvent { + message: "Session persistence is disabled; cannot rename session.".to_string(), + codex_error_info: Some(CodexErrorInfo::Other), + }), + }; + sess.send_event_raw(event).await; + return; + }; + + if let Err(e) = recorder.set_session_title(title).await { + let event = Event { + id: sub_id, + msg: EventMsg::Error(ErrorEvent { + message: format!("Failed to set session title: {e}"), + codex_error_info: Some(CodexErrorInfo::Other), + }), + }; + sess.send_event_raw(event).await; + } + } + pub async fn shutdown(sess: &Arc, sub_id: String) -> bool { sess.abort_all_tasks(TurnAbortReason::Interrupted).await; sess.services diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index f6a3cca108..2f9c698394 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -1886,6 +1886,23 @@ impl ChatWidget { } } + fn show_rename_prompt(&mut self) { + let tx = self.app_event_tx.clone(); + let view = CustomPromptView::new( + "Rename session".to_string(), + "Type a new name and press Enter".to_string(), + None, + Box::new(move |title: String| { + tx.send(AppEvent::InsertHistoryCell(Box::new( + history_cell::new_info_event(format!("Session renamed to \"{title}\""), None), + ))); + tx.send(AppEvent::CodexOp(Op::SetSessionTitle { title })); + }), + ); + + self.bottom_pane.show_view(Box::new(view)); + } + pub(crate) fn handle_paste(&mut self, text: String) { self.bottom_pane.handle_paste(text); } diff --git a/codex-rs/tui2/src/chatwidget.rs b/codex-rs/tui2/src/chatwidget.rs index 254b17129a..e0777dc123 100644 --- a/codex-rs/tui2/src/chatwidget.rs +++ b/codex-rs/tui2/src/chatwidget.rs @@ -1713,6 +1713,23 @@ impl ChatWidget { } } + fn show_rename_prompt(&mut self) { + let tx = self.app_event_tx.clone(); + let view = CustomPromptView::new( + "Rename session".to_string(), + "Type a new name and press Enter".to_string(), + None, + Box::new(move |title: String| { + tx.send(AppEvent::InsertHistoryCell(Box::new( + history_cell::new_info_event(format!("Session renamed to \"{title}\""), None), + ))); + tx.send(AppEvent::CodexOp(Op::SetSessionTitle { title })); + }), + ); + + self.bottom_pane.show_view(Box::new(view)); + } + pub(crate) fn handle_paste(&mut self, text: String) { self.bottom_pane.handle_paste(text); }