Dismiss stale app-server requests after remote resolution (#15134)

Dismiss stale TUI app-server approvals after remote resolution

When an approval, user-input prompt, or elicitation request is resolved
by another client, the TUI now dismisses the matching local UI instead
of leaving stale prompts behind and emitting a misleading local
cancellation.

This change teaches pending app-server request tracking to map
`serverRequest/resolved` notifications back to the concrete request type
and stable request key, then propagates that resolved request into TUI
prompt state. Approval, request-user-input, and MCP elicitation overlays
now drop the resolved current or queued request quietly, advance to the
next queued request when present, and avoid emitting abort/cancel events
for stale UI.

The latest update also retires matching prompts while they are still
deferred behind active streaming and suppresses buffered active-thread
requests whose app-server request id has already been resolved before
drain. `ChatWidget` removes a resolved request from both the deferred
interrupt queue and the materialized bottom-pane stack, while
active-thread request handling verifies the app-server request is still
pending before showing a prompt. Lifecycle events such as exec begin/end
remain queued so approved work can still render normally.

Tests cover resolved-request mapping, overlay dismissal behavior,
deferred prompt pruning for same-turn user input, exec approval IDs,
lifecycle-event retention, and the buffered active-thread ordering
regression.

Validation:
- `just fmt`
- `git diff --check`
- `cargo test -p codex-tui
resolved_buffered_approval_does_not_become_actionable_after_drain`
- `cargo test -p codex-tui
enqueue_primary_thread_session_replays_buffered_approval_after_attach`
- `cargo test -p codex-tui chatwidget::interrupts`
- `just fix -p codex-tui`

---------

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Eugene BrevdoandCodex authored and GitHub committed 2026-04-15 13:57:41 -07:00
1 parent ba36415a30
commit bc969b6516
12 files changed
+1164 -56

No files matched your search

+11
View File
@@ -43,6 +43,7 @@ use std::time::Duration;
use std::time::Instant;
use self::realtime::PendingSteerCompareKey;
use crate::app::app_server_requests::ResolvedAppServerRequest;
use crate::app_command::AppCommand;
use crate::app_event::RealtimeAudioDeviceKind;
use crate::app_server_approval_conversions::network_approval_context_to_core;
@@ -2163,6 +2164,16 @@ impl ChatWidget {
self.request_redraw();
}
pub(crate) fn dismiss_app_server_request(&mut self, request: &ResolvedAppServerRequest) {
// A remotely resolved request must not remain user-actionable. It may be
// materialized in the bottom pane or still deferred behind active streaming.
let removed_deferred = self.interrupts.remove_resolved_prompt(request);
let removed_visible = self.bottom_pane.dismiss_app_server_request(request);
if removed_deferred || removed_visible {
self.request_redraw();
}
}
pub(crate) fn open_feedback_consent(&mut self, category: crate::app_event::FeedbackCategory) {
let snapshot = self.feedback.snapshot(self.thread_id);
let params = crate::bottom_pane::feedback_upload_consent_params(