add missing fields to WebSearchAction and update app-server types (#10276)

- add `WebSearchAction` to app-server v2 types
- add `queries` to `WebSearchAction::Search` type

Updated tests.
This commit is contained in:
sayan-oai
2026-01-30 16:37:56 -08:00
committed by GitHub
parent 149f3aa27a
commit eb86663dcb
10 changed files with 95 additions and 10 deletions

View File

@@ -2121,7 +2121,11 @@ pub enum ThreadItem {
},
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase")]
WebSearch { id: String, query: String },
WebSearch {
id: String,
query: String,
action: Option<WebSearchAction>,
},
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase")]
ImageView { id: String, path: String },
@@ -2136,6 +2140,42 @@ pub enum ThreadItem {
ContextCompaction { id: String },
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(tag = "type", rename_all = "camelCase")]
#[ts(tag = "type", rename_all = "camelCase")]
pub enum WebSearchAction {
Search {
query: Option<String>,
queries: Option<Vec<String>>,
},
OpenPage {
url: Option<String>,
},
FindInPage {
url: Option<String>,
pattern: Option<String>,
},
#[serde(other)]
Other,
}
impl From<codex_protocol::models::WebSearchAction> for WebSearchAction {
fn from(value: codex_protocol::models::WebSearchAction) -> Self {
match value {
codex_protocol::models::WebSearchAction::Search { query, queries } => {
WebSearchAction::Search { query, queries }
}
codex_protocol::models::WebSearchAction::OpenPage { url } => {
WebSearchAction::OpenPage { url }
}
codex_protocol::models::WebSearchAction::FindInPage { url, pattern } => {
WebSearchAction::FindInPage { url, pattern }
}
codex_protocol::models::WebSearchAction::Other => WebSearchAction::Other,
}
}
}
impl From<CoreTurnItem> for ThreadItem {
fn from(value: CoreTurnItem) -> Self {
match value {
@@ -2165,6 +2205,7 @@ impl From<CoreTurnItem> for ThreadItem {
CoreTurnItem::WebSearch(search) => ThreadItem::WebSearch {
id: search.id,
query: search.query,
action: Some(WebSearchAction::from(search.action)),
},
CoreTurnItem::ContextCompaction(compaction) => {
ThreadItem::ContextCompaction { id: compaction.id }
@@ -2818,7 +2859,7 @@ mod tests {
use codex_protocol::items::TurnItem;
use codex_protocol::items::UserMessageItem;
use codex_protocol::items::WebSearchItem;
use codex_protocol::models::WebSearchAction;
use codex_protocol::models::WebSearchAction as CoreWebSearchAction;
use codex_protocol::protocol::NetworkAccess as CoreNetworkAccess;
use codex_protocol::user_input::UserInput as CoreUserInput;
use pretty_assertions::assert_eq;
@@ -2934,8 +2975,9 @@ mod tests {
let search_item = TurnItem::WebSearch(WebSearchItem {
id: "search-1".to_string(),
query: "docs".to_string(),
action: WebSearchAction::Search {
action: CoreWebSearchAction::Search {
query: Some("docs".to_string()),
queries: None,
},
});
@@ -2944,6 +2986,10 @@ mod tests {
ThreadItem::WebSearch {
id: "search-1".to_string(),
query: "docs".to_string(),
action: Some(WebSearchAction::Search {
query: Some("docs".to_string()),
queries: None,
}),
}
);
}

View File

@@ -450,7 +450,7 @@ Today both notifications carry an empty `items` array even when item events were
- `fileChange``{id, changes, status}` describing proposed edits; `changes` list `{path, kind, diff}` and `status` is `inProgress`, `completed`, `failed`, or `declined`.
- `mcpToolCall``{id, server, tool, status, arguments, result?, error?}` describing MCP calls; `status` is `inProgress`, `completed`, or `failed`.
- `collabToolCall``{id, tool, status, senderThreadId, receiverThreadId?, newThreadId?, prompt?, agentStatus?}` describing collab tool calls (`spawn_agent`, `send_input`, `wait`, `close_agent`); `status` is `inProgress`, `completed`, or `failed`.
- `webSearch``{id, query}` for a web search request issued by the agent.
- `webSearch``{id, query, action?}` for a web search request issued by the agent; `action` mirrors the Responses API web_search action payload (`search`, `open_page`, `find_in_page`) and may be omitted until completion.
- `imageView``{id, path}` emitted when the agent invokes the image viewer tool.
- `enteredReviewMode``{id, review}` sent when the reviewer starts; `review` is a short user-facing label such as `"current changes"` or the requested target description.
- `exitedReviewMode``{id, review}` emitted when the reviewer finishes; `review` is the full plain-text review (usually, overall notes plus bullet point findings).

View File

@@ -426,6 +426,7 @@ mod tests {
status: Some("completed".to_string()),
action: Some(WebSearchAction::Search {
query: Some("weather".to_string()),
queries: None,
}),
};
@@ -439,6 +440,7 @@ mod tests {
query: "weather".to_string(),
action: WebSearchAction::Search {
query: Some("weather".to_string()),
queries: None,
},
}
),

View File

@@ -1,8 +1,23 @@
use codex_protocol::models::WebSearchAction;
fn search_action_detail(query: &Option<String>, queries: &Option<Vec<String>>) -> String {
query.clone().filter(|q| !q.is_empty()).unwrap_or_else(|| {
let items = queries.as_ref();
let first = items
.and_then(|queries| queries.first())
.cloned()
.unwrap_or_default();
if items.is_some_and(|queries| queries.len() > 1) && !first.is_empty() {
format!("{first} ...")
} else {
first
}
})
}
pub fn web_search_action_detail(action: &WebSearchAction) -> String {
match action {
WebSearchAction::Search { query } => query.clone().unwrap_or_default(),
WebSearchAction::Search { query, queries } => search_action_detail(query, queries),
WebSearchAction::OpenPage { url } => url.clone().unwrap_or_default(),
WebSearchAction::FindInPage { url, pattern } => match (pattern, url) {
(Some(pattern), Some(url)) => format!("'{pattern}' in {url}"),

View File

@@ -1216,6 +1216,7 @@ async fn azure_responses_request_includes_store_and_reasoning_ids() {
status: Some("completed".into()),
action: Some(WebSearchAction::Search {
query: Some("weather".into()),
queries: None,
}),
});
prompt.input.push(ResponseItem::FunctionCall {

View File

@@ -255,6 +255,7 @@ async fn web_search_item_is_emitted() -> anyhow::Result<()> {
completed.action,
WebSearchAction::Search {
query: Some("weather seattle".to_string()),
queries: None,
}
);

View File

@@ -131,6 +131,7 @@ fn web_search_end_emits_item_completed() {
let query = "rust async await".to_string();
let action = WebSearchAction::Search {
query: Some(query.clone()),
queries: None,
};
let out = ep.collect_thread_events(&event(
"w1",
@@ -195,6 +196,7 @@ fn web_search_begin_then_end_reuses_item_id() {
};
let action = WebSearchAction::Search {
query: Some("rust async await".to_string()),
queries: None,
};
let end = ep.collect_thread_events(&event(
"w1",

View File

@@ -620,6 +620,9 @@ pub enum WebSearchAction {
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
query: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
queries: Option<Vec<String>>,
},
OpenPage {
#[serde(default, skip_serializing_if = "Option::is_none")]
@@ -1132,12 +1135,14 @@ mod tests {
"status": "completed",
"action": {
"type": "search",
"query": "weather seattle"
"query": "weather seattle",
"queries": ["weather seattle", "seattle weather now"]
}
}"#,
None,
Some(WebSearchAction::Search {
query: Some("weather seattle".into()),
queries: Some(vec!["weather seattle".into(), "seattle weather now".into()]),
}),
Some("completed".into()),
true,

View File

@@ -2469,6 +2469,7 @@ mod tests {
query: "find docs".into(),
action: WebSearchAction::Search {
query: Some("find docs".into()),
queries: None,
},
}),
};

View File

@@ -2294,7 +2294,10 @@ mod tests {
let cell = new_web_search_call(
"call-1".to_string(),
query.clone(),
WebSearchAction::Search { query: Some(query) },
WebSearchAction::Search {
query: Some(query),
queries: None,
},
);
let rendered = render_lines(&cell.display_lines(64)).join("\n");
@@ -2308,7 +2311,10 @@ mod tests {
let cell = new_web_search_call(
"call-1".to_string(),
query.clone(),
WebSearchAction::Search { query: Some(query) },
WebSearchAction::Search {
query: Some(query),
queries: None,
},
);
let rendered = render_lines(&cell.display_lines(64));
@@ -2327,7 +2333,10 @@ mod tests {
let cell = new_web_search_call(
"call-1".to_string(),
query.clone(),
WebSearchAction::Search { query: Some(query) },
WebSearchAction::Search {
query: Some(query),
queries: None,
},
);
let rendered = render_lines(&cell.display_lines(64));
@@ -2341,7 +2350,10 @@ mod tests {
let cell = new_web_search_call(
"call-1".to_string(),
query.clone(),
WebSearchAction::Search { query: Some(query) },
WebSearchAction::Search {
query: Some(query),
queries: None,
},
);
let rendered = render_lines(&cell.transcript_lines(64)).join("\n");