Persist resume metadata in compaction checkpoints (#47323)

## Why

Compaction companion records do not represent all resume state, including the last started turn and previous turn settings. The selected multi-agent runtime also needs a persisted value when no companion turn context is present.

## What changed

- Add optional `resume_metadata` to `CompactedItem`, recording the multi-agent version, last started turn ID, and previous turn settings. Preserve the distinction between absent metadata and explicitly empty values while retaining compatibility with older records.
- Capture this metadata under the settings persistence lock alongside the compaction checkpoint.
- Rewrite inherited metadata to use the child agent's runtime, and clear previous turn settings when the child must rebuild context.
- Clear pending continuation state before delivering acceptance of standalone settings updates, while holding the persistence lock.

## Testing

Add coverage for metadata round trips with empty values, legacy reader compatibility, checkpoint companion records, inherited fork metadata, and continuation invalidation while settings event delivery is blocked.

GitOrigin-RevId: f30b7feaa7e2fdb5b65fc3c53b37fd7a7fe27903
This commit is contained in:
Owen Lin
2026-09-22 16:35:30 +00:00
committed by copyberry
parent 5a11c45606
commit 286d4ecf44
23 changed files with 340 additions and 63 deletions
+19 -14
View File
@@ -39,20 +39,25 @@ pub(super) async fn update(
overrides: ThreadSettingsOverrides,
) {
let updates = prepare_update(overrides);
if let Err(error) = apply_update(session, submission_id.clone(), updates).await {
session
.send_event_raw(Event {
id: submission_id,
msg: EventMsg::Error(ErrorEvent {
misalignment: None,
message: format!("invalid thread settings override: {error}"),
codex_error_info: Some(CodexErrorInfo::BadRequest),
}),
})
.await;
} else {
// Standalone settings changes supersede a pending automatic continuation.
session.state.lock().await.last_started_turn_id = None;
let _settings_guard = acquire_persistence_lock(session).await;
match session.update_settings(updates).await {
Ok(commit) => {
// Standalone settings changes supersede a pending automatic continuation.
session.state.lock().await.last_started_turn_id = None;
emit_applied(session, submission_id, commit.snapshot).await;
}
Err(error) => {
session
.send_event_raw(Event {
id: submission_id,
msg: EventMsg::Error(ErrorEvent {
misalignment: None,
message: format!("invalid thread settings override: {error}"),
codex_error_info: Some(CodexErrorInfo::BadRequest),
}),
})
.await;
}
}
}