Allow naming forked chats from the TUI (#36036)

## What changed

- Accept an optional thread name with `/fork`, trimming surrounding whitespace.
- Apply the name to the newly forked thread and update the active session metadata.
- Keep the successful fork active and show an error when naming it fails.

## Testing

- Cover unnamed and named `/fork` dispatch, persisted names, and naming failures.

GitOrigin-RevId: 1799576f82ef0856f992a0378f041e4bab89621b
This commit is contained in:
Eric Traut authored and copyberry committed 2026-07-29 22:24:39 +00:00
1 parent 9cf6b3905c
commit a1286d12a2
8 files changed
+143 -22

No files matched your search

@@ -242,7 +242,8 @@ impl ChatWidget {
self.app_event_tx.send(AppEvent::OpenResumePicker);
}
SlashCommand::Fork => {
self.app_event_tx.send(AppEvent::ForkCurrentSession);
self.app_event_tx
.send(AppEvent::ForkCurrentSession { name: None });
}
SlashCommand::App => {
let Some(thread_id) = self.thread_id else {
@@ -744,6 +745,11 @@ impl ChatWidget {
name: Some(trimmed.to_string()),
});
}
SlashCommand::Fork if !trimmed.is_empty() => {
self.app_event_tx.send(AppEvent::ForkCurrentSession {
name: Some(trimmed.to_string()),
});
}
SlashCommand::Plan if !trimmed.is_empty() => {
if !self.apply_plan_slash_command() {
return;
@@ -2547,7 +2547,26 @@ async fn slash_fork_requests_current_fork() {
chat.dispatch_command(SlashCommand::Fork);
assert_matches!(rx.try_recv(), Ok(AppEvent::ForkCurrentSession));
assert_matches!(
rx.try_recv(),
Ok(AppEvent::ForkCurrentSession { name: None })
);
}
#[tokio::test]
async fn slash_fork_with_name_requests_named_fork() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
chat.bottom_pane
.set_composer_text("/fork Add User ".to_string(), Vec::new(), Vec::new());
chat.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
assert_matches!(
rx.try_recv(),
Ok(AppEvent::ForkCurrentSession {
name: Some(name)
}) if name == "Add User"
);
}
#[tokio::test]