Skip to content

Commit b4253cc

Browse files
charley-oaicodex
andcommitted
core: defer fork context injection to first turn
Co-authored-by: Codex <noreply@openai.com>
1 parent 8b01c21 commit b4253cc

File tree

3 files changed

+41
-22
lines changed

3 files changed

+41
-22
lines changed

codex-rs/core/src/codex.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2178,15 +2178,6 @@ impl Session {
21782178
self.persist_rollout_items(&rollout_items).await;
21792179
}
21802180

2181-
// Append the current session's initial context after the reconstructed history.
2182-
let initial_context = self.build_initial_context(&turn_context).await;
2183-
self.record_conversation_items(&turn_context, &initial_context)
2184-
.await;
2185-
{
2186-
let mut state = self.state.lock().await;
2187-
state.note_model_visible_turn_context(turn_context.to_turn_context_item());
2188-
}
2189-
21902181
// Forked threads should remain file-backed immediately after startup.
21912182
self.ensure_rollout_materialized().await;
21922183

codex-rs/core/src/codex_tests.rs

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,18 +1146,12 @@ async fn recompute_token_usage_updates_model_context_window() {
11461146
#[tokio::test]
11471147
async fn record_initial_history_reconstructs_forked_transcript() {
11481148
let (session, turn_context) = make_session_and_context().await;
1149-
let (rollout_items, mut expected) = sample_rollout(&session, &turn_context).await;
1149+
let (rollout_items, expected) = sample_rollout(&session, &turn_context).await;
11501150

11511151
session
11521152
.record_initial_history(InitialHistory::Forked(rollout_items))
11531153
.await;
11541154

1155-
let reconstruction_turn = session.new_default_turn().await;
1156-
expected.extend(
1157-
session
1158-
.build_initial_context(reconstruction_turn.as_ref())
1159-
.await,
1160-
);
11611155
let history = session.state.lock().await.clone_history();
11621156
assert_eq!(expected, history.raw_items());
11631157
}
@@ -1284,7 +1278,7 @@ async fn fork_startup_context_then_first_turn_diff_snapshot() -> anyhow::Result<
12841278
}
12851279

12861280
#[tokio::test]
1287-
async fn record_initial_history_forked_advances_previous_turn_settings_to_current_baseline() {
1281+
async fn record_initial_history_forked_preserves_previous_turn_settings_until_first_turn() {
12881282
let (session, turn_context) = make_session_and_context().await;
12891283
let previous_model = "forked-rollout-model";
12901284
let previous_context_item = TurnContextItem {
@@ -1327,7 +1321,7 @@ async fn record_initial_history_forked_advances_previous_turn_settings_to_curren
13271321
text_elements: Vec::new(),
13281322
},
13291323
)),
1330-
RolloutItem::TurnContext(previous_context_item),
1324+
RolloutItem::TurnContext(previous_context_item.clone()),
13311325
RolloutItem::EventMsg(EventMsg::TurnComplete(
13321326
codex_protocol::protocol::TurnCompleteEvent {
13331327
turn_id,
@@ -1340,13 +1334,49 @@ async fn record_initial_history_forked_advances_previous_turn_settings_to_curren
13401334
.record_initial_history(InitialHistory::Forked(rollout_items))
13411335
.await;
13421336

1337+
assert_eq!(
1338+
session.previous_turn_settings().await,
1339+
Some(PreviousTurnSettings {
1340+
model: previous_model.to_string(),
1341+
realtime_active: Some(turn_context.realtime_active),
1342+
})
1343+
);
1344+
assert_eq!(
1345+
serde_json::to_value(session.reference_context_item().await)
1346+
.expect("serialize forked reference context item"),
1347+
serde_json::to_value(Some(previous_context_item.clone()))
1348+
.expect("serialize expected forked reference context item")
1349+
);
1350+
1351+
let history_before_update = session.clone_history().await.raw_items().to_vec();
1352+
let update_items = session
1353+
.build_settings_update_items(Some(&previous_context_item), &turn_context)
1354+
.await;
1355+
assert!(!update_items.is_empty());
1356+
1357+
session
1358+
.record_context_updates_and_set_reference_context_item(&turn_context)
1359+
.await;
1360+
1361+
let mut expected_history = history_before_update;
1362+
expected_history.extend(update_items);
1363+
assert_eq!(
1364+
session.clone_history().await.raw_items().to_vec(),
1365+
expected_history
1366+
);
13431367
assert_eq!(
13441368
session.previous_turn_settings().await,
13451369
Some(PreviousTurnSettings {
13461370
model: turn_context.model_info.slug.clone(),
13471371
realtime_active: Some(turn_context.realtime_active),
13481372
})
13491373
);
1374+
assert_eq!(
1375+
serde_json::to_value(session.reference_context_item().await)
1376+
.expect("serialize current reference context item"),
1377+
serde_json::to_value(Some(turn_context.to_turn_context_item()))
1378+
.expect("serialize expected current reference context item")
1379+
);
13501380
}
13511381

13521382
#[tokio::test]

codex-rs/core/src/snapshots/codex_core__codex_tests__fork_startup_context_then_first_turn_diff.snap

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ Scenario: First request after fork when fork startup changes approval policy and
99
00:message/developer:<permissions instructions>\nFilesystem sandboxing defines which files can be read or written. `sandbox_mode` is `read-only`: The sandbox only permits reading files. Network access is restricted.\n# Escalation Requests\n\nCommands are run outside the sandbox if they are approved by the user, or match an existing rule that allows it to run unrestricted. The command string is split into independent command segments at shell control operators, including but not limited to:\n\n- Pipes: |\n- Logical operators: &&, ||\n- Command separators: ;\n- Subshell boundaries: (...), $(...)\n\nEach resulting segment is evaluated independently for sandbox restrictions and approval requirements.\n\nExample:\n\ngit pull | tee output.txt\n\nThis is treated as two command segments:\n\n["git", "pull"]\n\n["tee", "output.txt"]\n\nCommands that use more advanced shell features like redirection (>, >>, <), substitutions ($(...), ...), environment variables (FOO=bar), or wildcard patterns (*, ?) will not be evaluated against rules, to limit the scope of what an approved rule allows.\n\n## How to request escalation\n\nIMPORTANT: To request approval to execute a command that will require escalated privileges:\n\n- Provide the `sandbox_permissions` parameter with the value `"require_escalated"`\n- Include a short question asking the user if they want to allow the action in `justification` parameter. e.g. "Do you want to download and install dependencies for this project?"\n- Optionally suggest a `prefix_rule` - this will be shown to the user with an option to persist the rule approval for future sessions.\n\nIf you run a command that is important to solving the user's query, but it fails because of sandboxing or with a likely sandbox-related network error (for example DNS/host resolution, registry/index access, or dependency download failure), rerun the command with "require_escalated". ALWAYS proceed to use the `justification` parameter - do not message the user before requesting approval for the command.\n\n## When to request escalation\n\nWhile commands are running inside the sandbox, here are some scenarios that will require escalation outside the sandbox:\n\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /var)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing or with a likely sandbox-related network error (for example DNS/host resolution, registry/index access, or dependency download failure), rerun the command with `require_escalated`. ALWAYS proceed to use the `sandbox_permissions` and `justification` parameters. do not message the user before requesting approval for the command.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for.\n- Be judicious with escalating, but if completing the user's request requires it, you should do so - don't try and circumvent approvals by using other tools.\n\n## prefix_rule guidance\n\nWhen choosing a `prefix_rule`, request one that will allow you to fulfill similar requests from the user in the future without re-requesting escalation. It should be categorical and reasonably scoped to similar capabilities. You should rarely pass the entire command into `prefix_rule`.\n\n### Banned prefix_rules \nAvoid requesting overly broad prefixes that the user would be ill-advised to approve. For example, do not request ["python3"], ["python", "-"], or other similar prefixes that would allow arbitrary scripting.\nNEVER provide a prefix_rule argument for destructive commands like rm.\nNEVER provide a prefix_rule if your command uses a heredoc or herestring. \n\n### Examples\nGood examples of prefixes:\n- ["npm", "run", "dev"]\n- ["gh", "pr", "check"]\n- ["cargo", "test"]\n</permissions instructions>
1010
01:message/user:<environment_context>\n <cwd><CWD></cwd>\n <shell>zsh</shell>\n <current_date><CURRENT_DATE></current_date>\n <timezone>America/Los_Angeles</timezone>\n</environment_context>
1111
02:message/user:fork seed
12-
03:message/developer:<permissions instructions>\nFilesystem sandboxing defines which files can be read or written. `sandbox_mode` is `read-only`: The sandbox only permits reading files. Network access is restricted.\n Approvals are your mechanism to get user consent to run shell commands without the sandbox. `approval_policy` is `unless-trusted`: The harness will escalate most commands for user approval, apart from a limited allowlist of safe "read" commands.\n</permissions instructions>
13-
04:message/user:<environment_context>\n <cwd><CWD></cwd>\n <shell>zsh</shell>\n <current_date><CURRENT_DATE></current_date>\n <timezone>America/Los_Angeles</timezone>\n</environment_context>
14-
05:message/developer[2]:
12+
03:message/developer[2]:
1513
[01] <permissions instructions>\nFilesystem sandboxing defines which files can be read or written. `sandbox_mode` is `read-only`: The sandbox only permits reading files. Network access is restricted.\nApproval policy is currently never. Do not provide the `sandbox_permissions` for any reason, commands will be rejected.\n</permissions instructions>
1614
[02] <collaboration_mode>Fork turn collaboration instructions.</collaboration_mode>
17-
06:message/user:after fork
15+
04:message/user:after fork

0 commit comments

Comments
 (0)