Record interrupted turns in managed daemon recovery snapshots (#45807)

## Why

Managed daemon recovery snapshots previously saved only loaded thread IDs, without identifying active turns or preserving their turn-specific options.

## What changed

- Capture regular, uncanceled turns after their input is recorded, saving the turn ID, output schema, service tier, and cyber access program alongside persisted thread IDs.
- Store interruption metadata atomically in the existing candidate array format so older servers can still read thread candidates.
- Begin snapshotting once admitted operations drain, while turns may still be running. Run snapshot collection and thread listener attachment independently of the event loop to keep forced shutdown responsive.

## Testing

Add coverage for running, completed, canceled, and compacting turns; recovery readiness for automatic and user turns; admitted resumes during shutdown; forced shutdown with a blocked rollout writer and child listener attachment; and legacy candidate-array compatibility.

GitOrigin-RevId: ed46342c3a5c71b09c48fa9acece2f15ae748e2f
This commit is contained in:
Eric Traut
2026-09-15 23:31:44 +00:00
committed by copyberry
parent 7f83d4922d
commit 4d2807023a
12 changed files with 407 additions and 38 deletions
+5
View File
@@ -497,6 +497,11 @@ impl CodexThread {
self.session.inject_if_running(items).await
}
/// Captures a regular turn only after its input is recorded. The caller must flush the rollout.
pub async fn interrupted_turn(&self) -> Option<(String, TurnStartOptions)> {
self.session.interrupted_turn().await
}
/// Returns the trusted root when the expected turn is currently active.
pub async fn active_turn_root(&self, expected_turn_id: &str) -> Option<String> {
let active = self.session.active_turn.lock().await;