From fea2d8b18f5fade817d0d3a9cf4f278b6210ab01 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Mon, 27 Jul 2026 14:08:07 +0000 Subject: [PATCH] Skip restored token usage replay for exec resumes (#35621) ## What changed Set `excludeTurns` on `codex exec` thread resume requests so the app server does not return reconstructed turns or replay their restored token usage. ## Testing Extend the `resume --last` integration test to verify that resuming a session emits only the live turn's `thread/tokenUsage/updated` event. GitOrigin-RevId: 3347071142e7453ea9e3ac9ac292aa5384d98430 --- codex-rs/exec/src/lib.rs | 1 + codex-rs/exec/tests/suite/resume.rs | 30 +++++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index b022e6c467..5073c31904 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -1115,6 +1115,7 @@ fn thread_resume_params_from_config( sandbox: sandbox.flatten(), permissions, config: thread_config_overrides_from_config(config), + exclude_turns: true, ..ThreadResumeParams::default() } } diff --git a/codex-rs/exec/tests/suite/resume.rs b/codex-rs/exec/tests/suite/resume.rs index 25e3b65979..fc4720fc18 100644 --- a/codex-rs/exec/tests/suite/resume.rs +++ b/codex-rs/exec/tests/suite/resume.rs @@ -133,7 +133,18 @@ async fn exec_resume_last_appends_to_existing_file() -> anyhow::Result<()> { let test = test_codex_exec(); let server = MockServer::start().await; - let _response_mock = mount_exec_responses(&server, /*count*/ 2).await; + let _response_mock = responses::mount_sse_sequence( + &server, + vec![ + responses::sse(vec![ + responses::ev_response_created("resp-exec-0"), + responses::ev_assistant_message("msg-exec-0", "exec response"), + responses::ev_completed_with_tokens("resp-exec-0", /*total_tokens*/ 7), + ]), + exec_sse_response(/*index*/ 1), + ], + ) + .await; let repo_root = exec_repo_root()?; // 1) First run: create a session with a unique marker in the content. @@ -157,15 +168,26 @@ async fn exec_resume_last_appends_to_existing_file() -> anyhow::Result<()> { let marker2 = format!("resume-last-2-{}", Uuid::new_v4()); let prompt2 = format!("echo {marker2}"); - test.cmd_with_server(&server) + let output = test + .cmd_with_server(&server) + .env("RUST_LOG", "codex_app_server::outgoing_message=trace") .arg("--skip-git-repo-check") .arg("-C") .arg(&repo_root) .arg(&prompt2) .arg("resume") .arg("--last") - .assert() - .success(); + .output() + .context("resume run should succeed")?; + let stderr = String::from_utf8_lossy(&output.stderr); + assert!(output.status.success(), "resume failed: {stderr}"); + assert_eq!( + stderr + .matches("app-server event: thread/tokenUsage/updated") + .count(), + 1, + "resume should not replay restored token usage: {stderr}" + ); // Ensure the same file was updated and contains both markers. let resumed_path = find_session_file_containing_marker(&sessions_dir, &marker2)