mirror of
https://github.com/openai/codex.git
synced 2026-10-30 19:18:49 +00:00
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:
@@ -70,7 +70,7 @@ pub(super) fn agent_nickname_candidates(config: &Config, role_name: Option<&str>
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn keep_forked_rollout_item(item: &RolloutItem, preserve_reference_context_item: bool) -> bool {
|
||||
fn keep_forked_rollout_item(item: &RolloutItem, preserve_context_baselines: bool) -> bool {
|
||||
match item {
|
||||
RolloutItem::ResponseItem(envelope) => match &envelope.item {
|
||||
ResponseItem::Message { role, phase, .. } => match role.as_str() {
|
||||
@@ -107,7 +107,7 @@ fn keep_forked_rollout_item(item: &RolloutItem, preserve_reference_context_item:
|
||||
// Full-history forks preserve the cached prompt prefix and can keep diffing
|
||||
// from the parent's durable baseline. Truncated forks drop part of that prompt,
|
||||
// so they must rebuild context on their first child turn.
|
||||
RolloutItem::TurnContext(_) | RolloutItem::WorldState(_) => preserve_reference_context_item,
|
||||
RolloutItem::TurnContext(_) | RolloutItem::WorldState(_) => preserve_context_baselines,
|
||||
// Child threads inherit model context, not the parent's cumulative usage state.
|
||||
RolloutItem::TokenUsageRecord(_) => false,
|
||||
RolloutItem::Compacted(_) | RolloutItem::EventMsg(_) | RolloutItem::SessionMeta(_) => true,
|
||||
@@ -936,9 +936,8 @@ impl LocalAgentControl {
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let mut preserve_reference_context_item =
|
||||
matches!(fork_mode, SpawnAgentForkMode::FullHistory);
|
||||
if preserve_reference_context_item {
|
||||
let mut preserve_context_baselines = matches!(fork_mode, SpawnAgentForkMode::FullHistory);
|
||||
if preserve_context_baselines {
|
||||
for item in forked_rollout_items.iter().rev() {
|
||||
let RolloutItem::Compacted(compacted) = item else {
|
||||
continue;
|
||||
@@ -946,7 +945,7 @@ impl LocalAgentControl {
|
||||
// Legacy checkpoints force the child to rebuild context regardless of the
|
||||
// live parent's reference baseline; an older superseded checkpoint does not.
|
||||
if compacted.replacement_history.is_none() {
|
||||
preserve_reference_context_item = false;
|
||||
preserve_context_baselines = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1001,7 +1000,7 @@ impl LocalAgentControl {
|
||||
// If the child will rebuild its initial context, drop the inherited
|
||||
// instructions; startup will add the current requirements and effort
|
||||
// instructions once.
|
||||
return preserve_reference_context_item;
|
||||
return preserve_context_baselines;
|
||||
}
|
||||
let (
|
||||
Some(parent_developer_instructions),
|
||||
@@ -1019,7 +1018,7 @@ impl LocalAgentControl {
|
||||
}
|
||||
|
||||
*replaced = true;
|
||||
let replacement = if preserve_reference_context_item {
|
||||
let replacement = if preserve_context_baselines {
|
||||
subagent_developer_instructions.as_str()
|
||||
} else {
|
||||
""
|
||||
@@ -1034,7 +1033,7 @@ impl LocalAgentControl {
|
||||
true
|
||||
};
|
||||
forked_rollout_items.retain_mut(|item| {
|
||||
if !keep_forked_rollout_item(item, preserve_reference_context_item)
|
||||
if !keep_forked_rollout_item(item, preserve_context_baselines)
|
||||
|| destination_history_mode == Some(ThreadHistoryMode::Paginated)
|
||||
&& matches!(
|
||||
&*item,
|
||||
@@ -1054,8 +1053,15 @@ impl LocalAgentControl {
|
||||
retain_forked_item(response_item, &mut replaced_parent_developer_instructions)
|
||||
}
|
||||
RolloutItem::Compacted(compacted) => {
|
||||
// This checkpoint belongs to the inherited parent prefix.
|
||||
// This compaction becomes part of the subagent's initial history. Rewrite its
|
||||
// metadata to describe the child rather than the parent.
|
||||
compacted.latest_token_usage_record = None;
|
||||
if let Some(resume_metadata) = &mut compacted.resume_metadata {
|
||||
resume_metadata.multi_agent_version = Some(multi_agent_version);
|
||||
if !preserve_context_baselines {
|
||||
resume_metadata.previous_turn_settings = None;
|
||||
}
|
||||
}
|
||||
// Parent-local review evidence must not become the child's authorization.
|
||||
// Root user authorization is collected separately by the host.
|
||||
compacted.guardian_history = None;
|
||||
@@ -1097,7 +1103,7 @@ impl LocalAgentControl {
|
||||
// context omitted the parent's developer fragment, append the child's override so its
|
||||
// instructions still reach the model exactly once.
|
||||
if let Some(subagent_developer_instructions) = subagent_developer_instructions.as_ref()
|
||||
&& preserve_reference_context_item
|
||||
&& preserve_context_baselines
|
||||
&& !replaced_parent_developer_instructions
|
||||
&& !subagent_developer_instructions.is_empty()
|
||||
&& parent_thread
|
||||
@@ -1111,7 +1117,7 @@ impl LocalAgentControl {
|
||||
));
|
||||
forked_rollout_items.push(RolloutItem::ResponseItem(developer_message.into()));
|
||||
}
|
||||
if preserve_reference_context_item
|
||||
if preserve_context_baselines
|
||||
&& multi_agent_version == MultiAgentVersion::V2
|
||||
&& let Some(subagent_usage_hint) = options
|
||||
.multi_agent_v2_usage_hints
|
||||
|
||||
Reference in New Issue
Block a user