mirror of
https://github.com/openai/codex.git
synced 2026-04-29 17:06:51 +00:00
fix: handle all web_search actions and in progress invocations (#9960)
### Summary - Parse all `web_search` tool actions (`search`, `find_in_page`, `open_page`). - Previously we only parsed + displayed `search`, which made the TUI appear to pause when the other actions were being used. - Show in progress `web_search` calls as `Searching the web` - Previously we only showed completed tool calls <img width="308" height="149" alt="image" src="https://github.com/user-attachments/assets/90a4e8ff-b06a-48ff-a282-b57b31121845" /> ### Tests Added + updated tests, tested locally ### Follow ups Update VSCode extension to display these as well
This commit is contained in:
@@ -43,6 +43,8 @@ use codex_core::protocol::FileChange;
|
||||
use codex_core::protocol::McpAuthStatus;
|
||||
use codex_core::protocol::McpInvocation;
|
||||
use codex_core::protocol::SessionConfiguredEvent;
|
||||
use codex_core::web_search::web_search_detail;
|
||||
use codex_protocol::models::WebSearchAction;
|
||||
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
|
||||
use codex_protocol::plan_tool::PlanItemArg;
|
||||
use codex_protocol::plan_tool::StepStatus;
|
||||
@@ -1342,9 +1344,89 @@ pub(crate) fn new_active_mcp_tool_call(
|
||||
McpToolCallCell::new(call_id, invocation, animations_enabled)
|
||||
}
|
||||
|
||||
pub(crate) fn new_web_search_call(query: String) -> PrefixedWrappedHistoryCell {
|
||||
let text: Text<'static> = Line::from(vec!["Searched".bold(), " ".into(), query.into()]).into();
|
||||
PrefixedWrappedHistoryCell::new(text, "• ".dim(), " ")
|
||||
fn web_search_header(completed: bool) -> &'static str {
|
||||
if completed {
|
||||
"Searched"
|
||||
} else {
|
||||
"Searching the web"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct WebSearchCell {
|
||||
call_id: String,
|
||||
query: String,
|
||||
action: Option<WebSearchAction>,
|
||||
start_time: Instant,
|
||||
completed: bool,
|
||||
animations_enabled: bool,
|
||||
}
|
||||
|
||||
impl WebSearchCell {
|
||||
pub(crate) fn new(
|
||||
call_id: String,
|
||||
query: String,
|
||||
action: Option<WebSearchAction>,
|
||||
animations_enabled: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
call_id,
|
||||
query,
|
||||
action,
|
||||
start_time: Instant::now(),
|
||||
completed: false,
|
||||
animations_enabled,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn call_id(&self) -> &str {
|
||||
&self.call_id
|
||||
}
|
||||
|
||||
pub(crate) fn update(&mut self, action: WebSearchAction, query: String) {
|
||||
self.action = Some(action);
|
||||
self.query = query;
|
||||
}
|
||||
|
||||
pub(crate) fn complete(&mut self) {
|
||||
self.completed = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl HistoryCell for WebSearchCell {
|
||||
fn display_lines(&self, width: u16) -> Vec<Line<'static>> {
|
||||
let bullet = if self.completed {
|
||||
"•".dim()
|
||||
} else {
|
||||
spinner(Some(self.start_time), self.animations_enabled)
|
||||
};
|
||||
let header = web_search_header(self.completed);
|
||||
let detail = web_search_detail(self.action.as_ref(), &self.query);
|
||||
let text: Text<'static> = if detail.is_empty() {
|
||||
Line::from(vec![header.bold()]).into()
|
||||
} else {
|
||||
Line::from(vec![header.bold(), " ".into(), detail.into()]).into()
|
||||
};
|
||||
PrefixedWrappedHistoryCell::new(text, vec![bullet, " ".into()], " ").display_lines(width)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_active_web_search_call(
|
||||
call_id: String,
|
||||
query: String,
|
||||
animations_enabled: bool,
|
||||
) -> WebSearchCell {
|
||||
WebSearchCell::new(call_id, query, None, animations_enabled)
|
||||
}
|
||||
|
||||
pub(crate) fn new_web_search_call(
|
||||
call_id: String,
|
||||
query: String,
|
||||
action: WebSearchAction,
|
||||
) -> WebSearchCell {
|
||||
let mut cell = WebSearchCell::new(call_id, query, Some(action), false);
|
||||
cell.complete();
|
||||
cell
|
||||
}
|
||||
|
||||
/// If the first content is an image, return a new cell with the image.
|
||||
@@ -1837,6 +1919,7 @@ mod tests {
|
||||
use codex_core::config::types::McpServerConfig;
|
||||
use codex_core::config::types::McpServerTransportConfig;
|
||||
use codex_core::protocol::McpAuthStatus;
|
||||
use codex_protocol::models::WebSearchAction;
|
||||
use codex_protocol::parse_command::ParsedCommand;
|
||||
use dirs::home_dir;
|
||||
use pretty_assertions::assert_eq;
|
||||
@@ -2060,8 +2143,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn web_search_history_cell_snapshot() {
|
||||
let query =
|
||||
"example search query with several generic words to exercise wrapping".to_string();
|
||||
let cell = new_web_search_call(
|
||||
"example search query with several generic words to exercise wrapping".to_string(),
|
||||
"call-1".to_string(),
|
||||
query.clone(),
|
||||
WebSearchAction::Search { query: Some(query) },
|
||||
);
|
||||
let rendered = render_lines(&cell.display_lines(64)).join("\n");
|
||||
|
||||
@@ -2070,8 +2157,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn web_search_history_cell_wraps_with_indented_continuation() {
|
||||
let query =
|
||||
"example search query with several generic words to exercise wrapping".to_string();
|
||||
let cell = new_web_search_call(
|
||||
"example search query with several generic words to exercise wrapping".to_string(),
|
||||
"call-1".to_string(),
|
||||
query.clone(),
|
||||
WebSearchAction::Search { query: Some(query) },
|
||||
);
|
||||
let rendered = render_lines(&cell.display_lines(64));
|
||||
|
||||
@@ -2086,7 +2177,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn web_search_history_cell_short_query_does_not_wrap() {
|
||||
let cell = new_web_search_call("short query".to_string());
|
||||
let query = "short query".to_string();
|
||||
let cell = new_web_search_call(
|
||||
"call-1".to_string(),
|
||||
query.clone(),
|
||||
WebSearchAction::Search { query: Some(query) },
|
||||
);
|
||||
let rendered = render_lines(&cell.display_lines(64));
|
||||
|
||||
assert_eq!(rendered, vec!["• Searched short query".to_string()]);
|
||||
@@ -2094,8 +2190,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn web_search_history_cell_transcript_snapshot() {
|
||||
let query =
|
||||
"example search query with several generic words to exercise wrapping".to_string();
|
||||
let cell = new_web_search_call(
|
||||
"example search query with several generic words to exercise wrapping".to_string(),
|
||||
"call-1".to_string(),
|
||||
query.clone(),
|
||||
WebSearchAction::Search { query: Some(query) },
|
||||
);
|
||||
let rendered = render_lines(&cell.transcript_lines(64)).join("\n");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user