mirror of
https://github.com/openai/codex.git
synced 2026-06-01 19:02:59 +00:00
Defer persistence of rollout file (#11028)
- Defer rollout persistence for fresh threads (`InitialHistory::New`): keep rollout events in memory and only materialize rollout file + state DB row on first `EventMsg::UserMessage`. - Keep precomputed rollout path available before materialization. - Change `thread/start` to build thread response from live config snapshot and optional precomputed path. - Improve pre-materialization behavior in app-server/TUI: clearer invalid-request errors for file-backed ops and a friendlier `/fork` “not ready yet” UX. - Update tests to match deferred semantics across start/read/archive/unarchive/fork/resume/review flows. - Improved resilience of user_shell test, which should be unrelated to this change but must be affected by timing changes For Reviewers: * The primary change is in recorder.rs * Most of the other changes were to fix up broken assumptions in existing tests Testing: * Manually tested CLI * Exercised app server paths by manually running IDE Extension with rebuilt CLI binary * Only user-visible change is that `/fork` in TUI generates visible error if used prior to first turn
This commit is contained in:
@@ -1445,46 +1445,57 @@ impl App {
|
||||
self.chat_widget
|
||||
.add_plain_history_lines(vec!["/fork".magenta().into()]);
|
||||
if let Some(path) = self.chat_widget.rollout_path() {
|
||||
match self
|
||||
.server
|
||||
.fork_thread(usize::MAX, self.config.clone(), path.clone())
|
||||
.await
|
||||
{
|
||||
Ok(forked) => {
|
||||
self.shutdown_current_thread().await;
|
||||
let init = self.chatwidget_init_for_forked_or_resumed_thread(
|
||||
tui,
|
||||
self.config.clone(),
|
||||
);
|
||||
self.chat_widget = ChatWidget::new_from_existing(
|
||||
init,
|
||||
forked.thread,
|
||||
forked.session_configured,
|
||||
);
|
||||
self.reset_thread_event_state();
|
||||
if let Some(summary) = summary {
|
||||
let mut lines: Vec<Line<'static>> =
|
||||
vec![summary.usage_line.clone().into()];
|
||||
if let Some(command) = summary.resume_command {
|
||||
let spans = vec![
|
||||
"To continue this session, run ".into(),
|
||||
command.cyan(),
|
||||
];
|
||||
lines.push(spans.into());
|
||||
// Fresh threads expose a precomputed path, but the file is
|
||||
// materialized lazily on first user message.
|
||||
if path.exists() {
|
||||
match self
|
||||
.server
|
||||
.fork_thread(usize::MAX, self.config.clone(), path.clone())
|
||||
.await
|
||||
{
|
||||
Ok(forked) => {
|
||||
self.shutdown_current_thread().await;
|
||||
let init = self.chatwidget_init_for_forked_or_resumed_thread(
|
||||
tui,
|
||||
self.config.clone(),
|
||||
);
|
||||
self.chat_widget = ChatWidget::new_from_existing(
|
||||
init,
|
||||
forked.thread,
|
||||
forked.session_configured,
|
||||
);
|
||||
self.reset_thread_event_state();
|
||||
if let Some(summary) = summary {
|
||||
let mut lines: Vec<Line<'static>> =
|
||||
vec![summary.usage_line.clone().into()];
|
||||
if let Some(command) = summary.resume_command {
|
||||
let spans = vec![
|
||||
"To continue this session, run ".into(),
|
||||
command.cyan(),
|
||||
];
|
||||
lines.push(spans.into());
|
||||
}
|
||||
self.chat_widget.add_plain_history_lines(lines);
|
||||
}
|
||||
self.chat_widget.add_plain_history_lines(lines);
|
||||
}
|
||||
Err(err) => {
|
||||
let path_display = path.display();
|
||||
self.chat_widget.add_error_message(format!(
|
||||
"Failed to fork current session from {path_display}: {err}"
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
let path_display = path.display();
|
||||
self.chat_widget.add_error_message(format!(
|
||||
"Failed to fork current session from {path_display}: {err}"
|
||||
));
|
||||
}
|
||||
} else {
|
||||
self.chat_widget.add_error_message(
|
||||
"A thread must contain at least one turn before it can be forked."
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
self.chat_widget
|
||||
.add_error_message("Current session is not ready to fork yet.".to_string());
|
||||
self.chat_widget.add_error_message(
|
||||
"A thread must contain at least one turn before it can be forked."
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
tui.frame_requester().schedule_frame();
|
||||
|
||||
@@ -6845,6 +6845,12 @@ impl ChatWidget {
|
||||
pub(crate) fn thread_name(&self) -> Option<String> {
|
||||
self.thread_name.clone()
|
||||
}
|
||||
|
||||
/// Returns the current thread's precomputed rollout path.
|
||||
///
|
||||
/// For fresh non-ephemeral threads this path may exist before the file is
|
||||
/// materialized; rollout persistence is deferred until the first user
|
||||
/// message is recorded.
|
||||
pub(crate) fn rollout_path(&self) -> Option<PathBuf> {
|
||||
self.current_rollout_path.clone()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user