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
+4 -2
View File
@@ -757,8 +757,10 @@ impl MessageProcessor {
self.thread_processor.thread_created_receiver()
}
pub(crate) async fn daemon_recovery_candidates(&self) -> Vec<String> {
self.thread_processor.daemon_recovery_candidates().await
pub(crate) async fn daemon_recovery_snapshot(
&self,
) -> codex_app_server_transport::daemon_recovery::RecoverySnapshot {
self.thread_processor.daemon_recovery_snapshot().await
}
pub(crate) async fn restore_daemon_threads(