Show activity for standalone web search calls (#24693)

## Why

Standalone `web.run` calls run in the extension, so they need normal
web-search progress activity while a request is in flight and durable
completed activity after a thread is reloaded.

Follow-up to #23823; uses the extension turn-item emission path added in
#24813.

## What changed

- Emit standalone `web.run` start/completion items through the host
turn-item emitter, preserving standard client delivery and rollout
persistence.
- Include useful completion detail for queries, image queries, and
literal-URL `open`/`find` commands.
- Render completed searches as `Searched the web` or `Searched the web
for <detail>`, with snapshot coverage for the detail-free case.
- Extend the app-server round-trip test to verify completed search
activity is reconstructed by `thread/read` after a fresh-process reload.

## Testing

- `just test -p codex-web-search-extension`
- `just test -p codex-app-server -E
"test(standalone_web_search_round_trips_encrypted_output)"`
This commit is contained in:
sayan-oai
2026-05-29 09:12:58 -07:00
committed by GitHub
Unverified
parent 5577a9e148
commit 96f1347fa3
10 changed files with 246 additions and 14 deletions
+5 -3
View File
@@ -4,7 +4,7 @@ use super::*;
fn web_search_header(completed: bool) -> &'static str {
if completed {
"Searched"
"Searched the web"
} else {
"Searching the web"
}
@@ -104,7 +104,8 @@ impl HistoryCell for WebSearchCell {
let text: Text<'static> = if detail.is_empty() {
Line::from(vec![header.bold()]).into()
} else {
Line::from(vec![header.bold(), " ".into(), detail.into()]).into()
let separator = if self.completed { " for " } else { " " };
Line::from(vec![header.bold(), separator.into(), detail.into()]).into()
};
PrefixedWrappedHistoryCell::new(text, vec![bullet, " ".into()], " ").display_lines(width)
}
@@ -115,7 +116,8 @@ impl HistoryCell for WebSearchCell {
if detail.is_empty() {
vec![Line::from(header)]
} else {
vec![Line::from(format!("{header} {detail}"))]
let separator = if self.completed { " for " } else { " " };
vec![Line::from(format!("{header}{separator}{detail}"))]
}
}
}
@@ -2,5 +2,5 @@
source: tui/src/history_cell.rs
expression: rendered
---
• Searched example search query with several generic words to
exercise wrapping
• Searched the web for example search query with several generic
words to exercise wrapping
@@ -2,5 +2,5 @@
source: tui/src/history_cell.rs
expression: rendered
---
• Searched example search query with several generic words to
exercise wrapping
• Searched the web for example search query with several generic
words to exercise wrapping
@@ -0,0 +1,5 @@
---
source: tui/src/history_cell.rs
expression: rendered
---
• Searched the web
+14 -3
View File
@@ -1084,6 +1084,14 @@ fn standalone_windows_update_available_history_cell_snapshot() {
insta::assert_snapshot!(rendered);
}
#[test]
fn web_search_history_cell_without_detail_snapshot() {
let cell = new_web_search_call("call-1".to_string(), String::new(), WebSearchAction::Other);
let rendered = render_lines(&cell.display_lines(/*width*/ 64)).join("\n");
insta::assert_snapshot!(rendered);
}
#[test]
fn web_search_history_cell_wraps_with_indented_continuation() {
let query = "example search query with several generic words to exercise wrapping".to_string();
@@ -1100,8 +1108,8 @@ fn web_search_history_cell_wraps_with_indented_continuation() {
assert_eq!(
rendered,
vec![
"• Searched example search query with several generic words to".to_string(),
" exercise wrapping".to_string(),
"• Searched the web for example search query with several generic".to_string(),
" words to exercise wrapping".to_string(),
]
);
}
@@ -1119,7 +1127,10 @@ fn web_search_history_cell_short_query_does_not_wrap() {
);
let rendered = render_lines(&cell.display_lines(/*width*/ 64));
assert_eq!(rendered, vec!["• Searched short query".to_string()]);
assert_eq!(
rendered,
vec!["• Searched the web for short query".to_string()]
);
}
#[test]