Make TUI async question replies compatible with desktop (#46486)

## Why

Async questions answered on another client should disappear from the TUI without losing drafts for other questions, even when questions have identical titles.

## What changed

- Send answers using the desktop reply envelope with stable per-question IDs, and resolve matching questions from committed messages and replayed history.
- Render replies as readable question-and-answer text in transcripts, queue previews, and input history.
- Preserve separate reply envelopes and message order when retrying rejected or interrupted input.
- Account for JSON escaping in input limits and fall back to plain text for oversized question IDs.

## Testing

Add regression coverage for cross-client dismissal, draft preservation, replay ordering, reply parsing, IDE context, distinct replies with identical text, and retry ordering. Update the async question scenario to use the reply envelope.

GitOrigin-RevId: 9b9e4b1e140508590401622d96cfc16fc192eb7c
This commit is contained in:
Eric Traut
2026-09-18 22:40:37 +00:00
committed by copyberry
parent 3d5b66c655
commit dbf478850f
23 changed files with 639 additions and 60 deletions
+9 -2
View File
@@ -119,10 +119,17 @@ pub(crate) fn thread_items_to_transcript_cells(
.map(codex_app_server_protocol::UserInput::into_core)
.collect(),
};
let message = item.message();
let reply_text = crate::async_question_reply::display_text(&message);
let text_elements = if reply_text.is_some() {
Vec::new()
} else {
item.text_elements()
};
cells.push(Arc::new(UserHistoryCell {
spoken: false,
message: item.message(),
text_elements: item.text_elements(),
message: reply_text.unwrap_or(message),
text_elements,
local_image_paths: item.local_image_paths(),
remote_image_urls: item.image_urls(),
}));