Skip to content

Commit ff322b4

Browse files
committed
Persist TurnContext after full-context reinjection
1 parent 9852e01 commit ff322b4

File tree

1 file changed

+87
-5
lines changed

1 file changed

+87
-5
lines changed

codex-rs/core/src/codex.rs

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2989,14 +2989,15 @@ impl Session {
29892989
self.record_conversation_items(turn_context, &context_items)
29902990
.await;
29912991
}
2992-
if previous_user_turn_model.is_none()
2993-
|| (!should_inject_full_context && !context_items.is_empty())
2992+
if should_inject_full_context
2993+
|| previous_user_turn_model.is_none()
2994+
|| !context_items.is_empty()
29942995
{
2995-
// Keep rollout TurnContext entries to:
2996+
// Keep rollout TurnContext entries for any turn that establishes or changes the
2997+
// persisted model-visible baseline:
29962998
// - the first real user turn (to recover `previous_model` on resume)
2999+
// - full-context reinjection after the baseline was cleared
29973000
// - steady-state turns that emitted explicit context diffs
2998-
// Full reinjection after compaction is tracked in runtime state only; resume will
2999-
// conservatively fall back to a missing baseline and reinject again if needed.
30003001
self.persist_rollout_items(&[RolloutItem::TurnContext(turn_context_item.clone())])
30013002
.await;
30023003
}
@@ -6382,6 +6383,10 @@ mod tests {
63826383
use crate::protocol::TokenCountEvent;
63836384
use crate::protocol::TokenUsage;
63846385
use crate::protocol::TokenUsageInfo;
6386+
use crate::protocol::UserMessageEvent;
6387+
use crate::rollout::policy::EventPersistenceMode;
6388+
use crate::rollout::recorder::RolloutRecorder;
6389+
use crate::rollout::recorder::RolloutRecorderParams;
63856390
use crate::state::TaskKind;
63866391
use crate::tasks::SessionTask;
63876392
use crate::tasks::SessionTaskContext;
@@ -8550,6 +8555,83 @@ mod tests {
85508555
assert!(text.contains("<model_switch>"));
85518556
}
85528557

8558+
#[tokio::test]
8559+
async fn record_context_updates_and_set_reference_context_item_persists_full_reinjection_to_rollout(
8560+
) {
8561+
let (session, previous_context) = make_session_and_context().await;
8562+
let next_model = if previous_context.model_info.slug == "gpt-5.1" {
8563+
"gpt-5"
8564+
} else {
8565+
"gpt-5.1"
8566+
};
8567+
let turn_context = previous_context
8568+
.with_model(next_model.to_string(), &session.services.models_manager)
8569+
.await;
8570+
let config = session.get_config().await;
8571+
let recorder = RolloutRecorder::new(
8572+
config.as_ref(),
8573+
RolloutRecorderParams::new(
8574+
ThreadId::default(),
8575+
None,
8576+
SessionSource::Exec,
8577+
BaseInstructions::default(),
8578+
Vec::new(),
8579+
EventPersistenceMode::Limited,
8580+
),
8581+
None,
8582+
None,
8583+
)
8584+
.await
8585+
.expect("create rollout recorder");
8586+
let rollout_path = recorder.rollout_path().to_path_buf();
8587+
{
8588+
let mut rollout = session.services.rollout.lock().await;
8589+
*rollout = Some(recorder);
8590+
}
8591+
8592+
session
8593+
.persist_rollout_items(&[RolloutItem::EventMsg(EventMsg::UserMessage(
8594+
UserMessageEvent {
8595+
message: "seed rollout".to_string(),
8596+
images: None,
8597+
local_images: Vec::new(),
8598+
text_elements: Vec::new(),
8599+
},
8600+
))])
8601+
.await;
8602+
{
8603+
let mut state = session.state.lock().await;
8604+
state.set_reference_context_item(None);
8605+
}
8606+
8607+
session
8608+
.record_context_updates_and_set_reference_context_item(
8609+
&turn_context,
8610+
Some(previous_context.model_info.slug.as_str()),
8611+
)
8612+
.await;
8613+
session.ensure_rollout_materialized().await;
8614+
session.flush_rollout().await;
8615+
8616+
let InitialHistory::Resumed(resumed) = RolloutRecorder::get_rollout_history(&rollout_path)
8617+
.await
8618+
.expect("read rollout history")
8619+
else {
8620+
panic!("expected resumed rollout history");
8621+
};
8622+
let persisted_turn_context = resumed.history.iter().find_map(|item| match item {
8623+
RolloutItem::TurnContext(ctx) => Some(ctx.clone()),
8624+
_ => None,
8625+
});
8626+
8627+
assert_eq!(
8628+
serde_json::to_value(persisted_turn_context)
8629+
.expect("serialize persisted turn context item"),
8630+
serde_json::to_value(Some(turn_context.to_turn_context_item()))
8631+
.expect("serialize expected turn context item")
8632+
);
8633+
}
8634+
85538635
#[tokio::test]
85548636
async fn run_user_shell_command_does_not_set_reference_context_item() {
85558637
let (session, _turn_context, rx) = make_session_and_context_with_rx().await;

0 commit comments

Comments
 (0)