mirror of
https://github.com/openai/codex.git
synced 2026-11-03 08:22:30 +00:00
Fix stale background terminal poll events (#23231)
## Why Issue #23214 reports `/ps` showing no background terminals while the status line still says it is waiting for a background terminal. The race is in core: `write_stdin` can poll a process that exits before the response returns. The process manager correctly returns `process_id: None`, but the handler still emitted a `TerminalInteraction` event using the requested session id, causing clients to believe a dead process was still being polled. Fixes #23214. ## What changed - Suppress `TerminalInteraction` events for empty `write_stdin` polls once `response.process_id` is `None`. - Continue emitting interactions for non-empty stdin, even if that input causes the process to exit before the response returns. - Extend the unified exec integration test to assert completed empty polls do not emit terminal interactions. ## Verification - `cargo test -p codex-core --test all unified_exec_emits_one_begin_and_one_end_event` - `cargo test -p codex-core --test all unified_exec_emits_terminal_interaction_for_write_stdin` `cargo test -p codex-core` currently aborts in unrelated `agent::control::tests::resume_agent_from_rollout_uses_edge_data_when_descendant_metadata_source_is_stale` with a reproducible stack overflow.
This commit is contained in:
1 parent
532b9c83ae
commit
e43a2e297f
5 files changed
+46
-11
No files matched your search
@@ -78,14 +78,21 @@ impl ToolExecutor<ToolInvocation> for WriteStdinHandler {
|
||||
FunctionCallError::RespondToModel(format!("write_stdin failed: {err}"))
|
||||
})?;
|
||||
|
||||
let interaction = TerminalInteractionEvent {
|
||||
call_id: response.event_call_id.clone(),
|
||||
process_id: args.session_id.to_string(),
|
||||
stdin: args.chars.clone(),
|
||||
};
|
||||
session
|
||||
.send_event(turn.as_ref(), EventMsg::TerminalInteraction(interaction))
|
||||
.await;
|
||||
// Empty stdin is a background poll, so emit it only while there is
|
||||
// still a live process for the UI to wait on. Non-empty stdin is a real
|
||||
// terminal interaction and should remain visible even if it completes
|
||||
// the process before the response returns.
|
||||
if !args.chars.is_empty() || response.process_id.is_some() {
|
||||
let process_id = response.process_id.unwrap_or(args.session_id);
|
||||
let interaction = TerminalInteractionEvent {
|
||||
call_id: response.event_call_id.clone(),
|
||||
process_id: process_id.to_string(),
|
||||
stdin: args.chars.clone(),
|
||||
};
|
||||
session
|
||||
.send_event(turn.as_ref(), EventMsg::TerminalInteraction(interaction))
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(boxed_tool_output(response))
|
||||
}
|
||||
|
||||
@@ -691,8 +691,8 @@ impl UnifiedExecProcessManager {
|
||||
|
||||
// After polling, refresh_process_state tells us whether the PTY is
|
||||
// still alive or has exited and been removed from the store; we thread
|
||||
// that through so the handler can tag TerminalInteraction with an
|
||||
// appropriate process_id and exit_code.
|
||||
// that through so the handler can tag or suppress TerminalInteraction
|
||||
// with an appropriate process_id and exit_code.
|
||||
let status = if let Some(status) = status_after_write {
|
||||
status
|
||||
} else {
|
||||
|
||||
Reference in new issue
Block a user