Compare commits

...

1 Commits

Author SHA1 Message Date
Dylan Hurd
3e086b73b5 fix(context) include yolo on resume 2026-01-08 17:45:12 -08:00
2 changed files with 99 additions and 7 deletions

View File

@@ -801,6 +801,7 @@ impl Session {
InitialHistory::Resumed(_) | InitialHistory::Forked(_) => {
let rollout_items = conversation_history.get_rollout_items();
let persist = matches!(conversation_history, InitialHistory::Forked(_));
let append_env_context = matches!(conversation_history, InitialHistory::Resumed(_));
// If resuming, warn when the last recorded model differs from the current one.
if let InitialHistory::Resumed(_) = conversation_history
@@ -845,6 +846,12 @@ impl Session {
state.set_token_info(Some(info));
}
if append_env_context {
let env_item = self.build_environment_context_item(&turn_context);
self.record_conversation_items(&turn_context, std::slice::from_ref(&env_item))
.await;
}
// If persisting, persist all rollout items as-is (recorder filters)
if persist && !rollout_items.is_empty() {
self.persist_rollout_items(&rollout_items).await;
@@ -1333,9 +1340,18 @@ impl Session {
}
}
fn build_environment_context_item(&self, turn_context: &TurnContext) -> ResponseItem {
let shell = self.user_shell();
ResponseItem::from(EnvironmentContext::new(
Some(turn_context.cwd.clone()),
Some(turn_context.approval_policy),
Some(turn_context.sandbox_policy.clone()),
shell.as_ref().clone(),
))
}
pub(crate) fn build_initial_context(&self, turn_context: &TurnContext) -> Vec<ResponseItem> {
let mut items = Vec::<ResponseItem>::with_capacity(3);
let shell = self.user_shell();
if let Some(developer_instructions) = turn_context.developer_instructions.as_deref() {
items.push(DeveloperInstructions::new(developer_instructions.to_string()).into());
}
@@ -1348,12 +1364,7 @@ impl Session {
.into(),
);
}
items.push(ResponseItem::from(EnvironmentContext::new(
Some(turn_context.cwd.clone()),
Some(turn_context.approval_policy),
Some(turn_context.sandbox_policy.clone()),
shell.as_ref().clone(),
)));
items.push(self.build_environment_context_item(turn_context));
items
}
@@ -2925,6 +2936,8 @@ mod tests {
async fn record_initial_history_reconstructs_resumed_transcript() {
let (session, turn_context) = make_session_and_context().await;
let (rollout_items, expected) = sample_rollout(&session, &turn_context);
let mut expected = expected;
expected.push(session.build_environment_context_item(&turn_context));
session
.record_initial_history(InitialHistory::Resumed(ResumedHistory {

View File

@@ -1,4 +1,7 @@
use anyhow::Result;
use codex_core::config::Constrained;
use codex_core::protocol::AskForApproval;
use codex_core::protocol::ENVIRONMENT_CONTEXT_OPEN_TAG;
use codex_core::protocol::EventMsg;
use codex_core::protocol::Op;
use codex_protocol::user_input::UserInput;
@@ -12,6 +15,7 @@ use core_test_support::responses::start_mock_server;
use core_test_support::skip_if_no_network;
use core_test_support::test_codex::test_codex;
use core_test_support::wait_for_event;
use pretty_assertions::assert_eq;
use std::sync::Arc;
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
@@ -120,3 +124,78 @@ async fn resume_includes_initial_messages_from_reasoning_events() -> Result<()>
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn resume_appends_environment_context_on_first_turn() -> Result<()> {
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let mut builder = test_codex();
let initial = builder.build(&server).await?;
let codex = Arc::clone(&initial.codex);
let home = initial.home.clone();
let rollout_path = initial.session_configured.rollout_path.clone();
let initial_sse = sse(vec![
ev_response_created("resp-initial"),
ev_assistant_message("msg-1", "Completed first turn"),
ev_completed("resp-initial"),
]);
mount_sse_once(&server, initial_sse).await;
codex
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "Record some messages".into(),
}],
final_output_json_schema: None,
})
.await?;
wait_for_event(&codex, |event| matches!(event, EventMsg::TaskComplete(_))).await;
let mut resume_builder = test_codex().with_config(|config| {
config.approval_policy = Constrained::allow_any(AskForApproval::Never);
});
let resumed = resume_builder.resume(&server, home, rollout_path).await?;
let resumed_sse = sse(vec![
ev_response_created("resp-resume"),
ev_assistant_message("msg-2", "Completed after resume"),
ev_completed("resp-resume"),
]);
let resumed_mock = mount_sse_once(&server, resumed_sse).await;
resumed
.codex
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "After resume".into(),
}],
final_output_json_schema: None,
})
.await?;
wait_for_event(&resumed.codex, |event| {
matches!(event, EventMsg::TaskComplete(_))
})
.await;
let body = resumed_mock.single_request().body_json();
let input = body["input"]
.as_array()
.expect("resume request input is an array");
let env_texts: Vec<&str> = input
.iter()
.filter_map(|item| item["content"][0]["text"].as_str())
.filter(|text| text.starts_with(ENVIRONMENT_CONTEXT_OPEN_TAG))
.collect();
assert_eq!(env_texts.is_empty(), false);
let last_env = env_texts.last().expect("environment context present");
assert_eq!(
last_env.contains("<approval_policy>never</approval_policy>"),
true,
);
Ok(())
}