TUI: route elicitation responses to request thread (#23241)

## Why

Fixes #21894.

When the TUI handles an MCP elicitation, the request payload already
includes the thread that generated the elicitation.
`ChatWidget::handle_elicitation_request_now` was ignoring that value and
using the currently visible chat thread instead. In a multi-session TUI,
that can send `resolve_elicitation` to an older visible thread rather
than the session that owns the pending elicitation, producing
`elicitation request not found` and leaving the prompt unresolved.

## What changed

- Parse `McpServerElicitationRequestParams.thread_id` in the ChatWidget
elicitation handler and use it for app-link, form, fallback approval,
and auto-decline resolution paths.
- Keep the existing visible-thread fallback only for malformed request
payloads with an invalid thread id.
- Update the invalid URL elicitation regression test so the visible
thread and request thread intentionally differ.
This commit is contained in:
Eric Traut
2026-05-18 11:33:13 -07:00
committed by GitHub
parent 4ac3ea20a2
commit 53a1f4c29e
2 changed files with 7 additions and 5 deletions

View File

@@ -4,13 +4,14 @@ use pretty_assertions::assert_eq;
#[tokio::test]
async fn invalid_url_elicitation_is_declined() {
let (mut chat, _app_event_tx, mut rx, _op_rx) = make_chatwidget_manual_with_sender().await;
let thread_id = ThreadId::new();
chat.thread_id = Some(thread_id);
let visible_thread_id = ThreadId::new();
let request_thread_id = ThreadId::new();
chat.thread_id = Some(visible_thread_id);
chat.handle_elicitation_request_now(
codex_app_server_protocol::RequestId::Integer(9),
codex_app_server_protocol::McpServerElicitationRequestParams {
thread_id: thread_id.to_string(),
thread_id: request_thread_id.to_string(),
turn_id: Some("turn-auth".to_string()),
server_name: "payments".to_string(),
request: codex_app_server_protocol::McpServerElicitationRequest::Url {
@@ -33,7 +34,7 @@ async fn invalid_url_elicitation_is_declined() {
content: None,
meta: None,
},
}) if op_thread_id == thread_id && server_name == "payments"
}) if op_thread_id == request_thread_id && server_name == "payments"
);
}

View File

@@ -341,7 +341,8 @@ impl ChatWidget {
server_name: params.server_name.clone(),
});
let thread_id = self.thread_id.unwrap_or_default();
let thread_id = ThreadId::from_string(&params.thread_id)
.unwrap_or_else(|_| self.thread_id.unwrap_or_default());
if let Some(params) = crate::bottom_pane::AppLinkViewParams::from_url_app_server_request(
thread_id,
&params.server_name,