From 006554b7d731cdc9fe3ca793831d3df3e5e7cbe7 Mon Sep 17 00:00:00 2001 From: Dotta Date: Fri, 18 Sep 2026 10:09:04 -0500 Subject: [PATCH] fix(runner): retain Codex sessions across late usage snapshots Co-Authored-By: Paperclip --- .../src/bin/fake-codex-app-server.rs | 6 ++++ .../crates/runner-core/src/codex_provider.rs | 28 +++++++++++++++++++ .../runner-core/tests/codex_provider.rs | 2 +- 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/bin/fake-codex-app-server.rs b/packages/paperclip-runner/runner/crates/runner-core/src/bin/fake-codex-app-server.rs index f39f934132..f93a42a216 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/bin/fake-codex-app-server.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/bin/fake-codex-app-server.rs @@ -1632,6 +1632,12 @@ fn run() -> Result<(), Box> { } if emit_post_completion_passive_statuses { for notification in [ + json!({ + "method": "thread/tokenUsage/updated", + "params": {"threadId": state.thread_id, "turnId": provider_turn_id, + "tokenUsage": {"total": {"inputTokens": 120, "outputTokens": 12}, + "last": {"inputTokens": 20, "outputTokens": 2}}} + }), json!({ "method": "remoteControl/status/changed", "params": {"status": "disabled", "environmentId": null} diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/codex_provider.rs b/packages/paperclip-runner/runner/crates/runner-core/src/codex_provider.rs index 1854de6d44..3f5cdd97ee 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/codex_provider.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/codex_provider.rs @@ -1260,6 +1260,9 @@ impl CodexProvider { | "thread/goal/updated" | "thread/goal/cleared" | "thread/tokenUsage/updated" + // poll() normalizes usage from this settled turn. + // It remains an accounting snapshot, not new work. + | "paperclip/resumeUsageSnapshot" | "thread/status/changed" | "turn/diff/updated" | "turn/plan/updated" @@ -3988,6 +3991,31 @@ done provider } + #[cfg(unix)] + #[test] + fn warm_attachment_accepts_normalized_usage_for_the_completed_turn() { + let mut provider = completion_tail_provider(); + provider + .restore_completed_turn_authority(true, Some(1), Some("reader-tail-1")) + .unwrap(); + provider.active_provider_turn_id = None; + provider + .pending_messages + .push_back(BufferedProviderMessage { + value: json!({ + "method": "thread/tokenUsage/updated", + "params": {"threadId": "reader-tail-thread", "turnId": "reader-tail-1", + "tokenUsage": {"total": {"inputTokens": 120, "outputTokens": 12}}} + }), + trace_frame_id: None, + }); + // poll() normalizes settled-turn usage to paperclip/resumeUsageSnapshot. + // That accounting fact is not new work and must not force replacement. + let result = provider.drain_completed_turn_tail_for_warm_attachment(); + provider.shutdown().unwrap(); + result.expect("historical usage must not break provider continuity"); + } + #[cfg(unix)] struct HeldTerminalReader { release: Option>, diff --git a/packages/paperclip-runner/runner/crates/runner-core/tests/codex_provider.rs b/packages/paperclip-runner/runner/crates/runner-core/tests/codex_provider.rs index 356e2fd140..ad104f68cd 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/tests/codex_provider.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/tests/codex_provider.rs @@ -3521,7 +3521,7 @@ fn durable_backend_rotates_tool_authority_for_fresh_run_attach() { } #[test] -fn durable_backend_drains_a_bounded_completed_turn_tail_during_warm_attach() { +fn durable_backend_drains_completed_turn_usage_and_passive_tail_during_warm_attach() { let directory = temporary_directory("durable-warm-attach-tail"); let config = provider_config( &directory,