mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[regression] Fix ephemeral turn backfill in exec (#16795)
Addresses #16781 Problem: `codex exec --ephemeral` backfilled empty `turn/completed` items with `thread/read(includeTurns=true)`, which app-server rejects for ephemeral threads. This is a regression introduced in the recent conversion of "exec" to use app server rather than call the core directly. Solution: Skip turn-item backfill for ephemeral exec threads while preserving the existing recovery path for non-ephemeral sessions.
This commit is contained in:
committed by
GitHub
Unverified
parent
ab58141e22
commit
fb41a79f37
@@ -790,8 +790,13 @@ async fn run_exec_session(args: ExecRunArgs) -> anyhow::Result<()> {
|
||||
error_seen = true;
|
||||
}
|
||||
|
||||
maybe_backfill_turn_completed_items(&client, &mut request_ids, &mut notification)
|
||||
.await;
|
||||
maybe_backfill_turn_completed_items(
|
||||
config.ephemeral,
|
||||
&client,
|
||||
&mut request_ids,
|
||||
&mut notification,
|
||||
)
|
||||
.await;
|
||||
|
||||
if should_process_notification(
|
||||
¬ification,
|
||||
@@ -1051,6 +1056,7 @@ fn should_process_notification(
|
||||
}
|
||||
|
||||
async fn maybe_backfill_turn_completed_items(
|
||||
thread_ephemeral: bool,
|
||||
client: &InProcessAppServerClient,
|
||||
request_ids: &mut RequestIdSequencer,
|
||||
notification: &mut ServerNotification,
|
||||
@@ -1059,12 +1065,13 @@ async fn maybe_backfill_turn_completed_items(
|
||||
// guaranteeing `turn/completed`. Because app-server currently emits that completion with an
|
||||
// empty `turn.items`, exec does one last `thread/read` here so human/json output can recover
|
||||
// the final message and reconcile any still-running items before shutdown.
|
||||
if !should_backfill_turn_completed_items(thread_ephemeral, notification) {
|
||||
return;
|
||||
}
|
||||
|
||||
let ServerNotification::TurnCompleted(payload) = notification else {
|
||||
return;
|
||||
};
|
||||
if !payload.turn.items.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let response = send_request_with_response::<ThreadReadResponse>(
|
||||
client,
|
||||
@@ -1091,6 +1098,19 @@ async fn maybe_backfill_turn_completed_items(
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true only when `exec` can safely recover missing turn items from
|
||||
/// rollout-backed thread history.
|
||||
fn should_backfill_turn_completed_items(
|
||||
thread_ephemeral: bool,
|
||||
notification: &ServerNotification,
|
||||
) -> bool {
|
||||
let ServerNotification::TurnCompleted(payload) = notification else {
|
||||
return false;
|
||||
};
|
||||
|
||||
!thread_ephemeral && payload.turn.items.is_empty()
|
||||
}
|
||||
|
||||
fn turn_items_for_thread(
|
||||
thread: &AppServerThread,
|
||||
turn_id: &str,
|
||||
|
||||
@@ -293,6 +293,25 @@ fn turn_items_for_thread_returns_matching_turn_items() {
|
||||
assert_eq!(turn_items_for_thread(&thread, "missing-turn"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_backfill_turn_completed_items_skips_ephemeral_threads() {
|
||||
let notification =
|
||||
ServerNotification::TurnCompleted(codex_app_server_protocol::TurnCompletedNotification {
|
||||
thread_id: "thread-1".to_string(),
|
||||
turn: codex_app_server_protocol::Turn {
|
||||
id: "turn-1".to_string(),
|
||||
items: Vec::new(),
|
||||
status: codex_app_server_protocol::TurnStatus::Completed,
|
||||
error: None,
|
||||
},
|
||||
});
|
||||
|
||||
assert!(!should_backfill_turn_completed_items(
|
||||
/*thread_ephemeral*/ true,
|
||||
¬ification
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn canceled_mcp_server_elicitation_response_uses_cancel_action() {
|
||||
let value = canceled_mcp_server_elicitation_response()
|
||||
|
||||
Reference in New Issue
Block a user