mirror of
https://github.com/openai/codex.git
synced 2026-11-13 06:45:07 +00:00
Preserve thread context when retrying or editing turns (#33211)
## What changed - Add experimental `beforeTurnId` support to `thread/fork` for copying history strictly before a turn, including an in-progress turn. - Use before-turn forks for TUI backtracking and safety retries so the replacement prompt retains thread context instead of recreating the branch from the preceding turn or a new thread. - Add experimental `deferGoalContinuation` support so safety-retry forks can inherit an active goal while waiting for the replacement turn before automatic continuation resumes. - Hide duplicated nested-review prompts when replaying history or selecting a prompt to edit. ## Testing - Cover before-turn truncation, fork validation, active-goal inheritance, TUI safety retries, backtracking, and nested-review replay. GitOrigin-RevId: 4915d85ae1929a0b8c40f3ed7dca29c7a5bdcdf3
This commit is contained in:
1 parent
e4711f2a3b
commit
d88db19144
25 files changed
+1343
-96
No files matched your search
@@ -93,6 +93,40 @@ impl GoalService {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Restores persisted goal state into the registered runtime for `thread_id`.
|
||||
pub async fn restore_thread_runtime_after_resume(
|
||||
&self,
|
||||
thread_id: ThreadId,
|
||||
) -> Result<(), GoalServiceError> {
|
||||
let runtime = self.runtime_for_thread(thread_id).ok_or_else(|| {
|
||||
GoalServiceError::Internal(format!(
|
||||
"goal runtime is unavailable for thread {thread_id}"
|
||||
))
|
||||
})?;
|
||||
runtime
|
||||
.restore_after_resume()
|
||||
.await
|
||||
.map_err(GoalServiceError::Internal)
|
||||
}
|
||||
|
||||
/// Flushes any in-flight goal accounting before a fork copies the source goal snapshot.
|
||||
pub async fn flush_thread_goal_progress_for_fork(
|
||||
&self,
|
||||
thread_id: ThreadId,
|
||||
) -> Result<(), GoalServiceError> {
|
||||
let Some(runtime) = self.runtime_for_thread(thread_id) else {
|
||||
return Ok(());
|
||||
};
|
||||
let _goal_state_permit = runtime
|
||||
.goal_state_permit()
|
||||
.await
|
||||
.map_err(GoalServiceError::Internal)?;
|
||||
runtime
|
||||
.prepare_external_goal_mutation()
|
||||
.await
|
||||
.map_err(GoalServiceError::Internal)
|
||||
}
|
||||
|
||||
pub async fn get_thread_goal(
|
||||
&self,
|
||||
state_db: &codex_state::StateRuntime,
|
||||
|
||||
@@ -207,6 +207,15 @@ where
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(err) = self
|
||||
.state_dbs
|
||||
.thread_goals()
|
||||
.clear_thread_goal_continuation_deferral(runtime.thread_id())
|
||||
.await
|
||||
{
|
||||
tracing::warn!("failed to clear deferred goal continuation: {err}");
|
||||
}
|
||||
|
||||
let accounting = runtime.accounting_state();
|
||||
accounting.start_turn(
|
||||
input.turn_id,
|
||||
|
||||
@@ -365,6 +365,17 @@ impl GoalRuntimeHandle {
|
||||
// change the goal after we read it but before the continuation launches.
|
||||
let _goal_state_permit = self.goal_state_permit().await?;
|
||||
|
||||
if self
|
||||
.inner
|
||||
.state_dbs
|
||||
.thread_goals()
|
||||
.has_thread_goal_continuation_deferral(self.thread_id())
|
||||
.await
|
||||
.map_err(|err| err.to_string())?
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let Some(thread_manager) = self.inner.thread_manager.upgrade() else {
|
||||
tracing::debug!("skipping goal continuation because thread manager is unavailable");
|
||||
return Ok(());
|
||||
|
||||
Reference in new issue
Block a user