core: migrate standalone web search to extension-owned turn items (#31525)

## Description

This PR migrates standalone web search onto the extension-owned
turn-item path introduced in #31283.

Standalone web search now emits `ExtensionItem::WebSearch` through
generic `TurnItem::Extension`, while app-server still exposes the
existing typed `ThreadItem::WebSearch` JSON shape. Hosted Responses API
web search stays on core-owned `TurnItem::WebSearch`.

## What changed

- Added `web_search::WebSearchItem` and `WebSearchAction` to
`codex-extension-items` under the stable `web.search` kind.
- Collapsed `ExtensionTurnItem` to generic `{ item, legacy_events }` now
that no typed extension special cases remain.
- Kept the existing `WebSearchBegin` / `WebSearchEnd` compatibility
events and canonical-first ordering.
- Updated app-server projection/history and generated TypeScript; the
app-server JSON schema is unchanged.
This commit is contained in:
Owen Lin authored and GitHub committed 2026-07-08 09:40:00 -07:00
1 parent e212cc95b0
commit a219b6fdb4
26 files changed
+279 -209

No files matched your search

+33
View File
@@ -3,6 +3,8 @@ use serde_json::json;
use super::ExtensionItem;
use super::image_generation::ImageGenerationItem;
use super::web_search::WebSearchAction;
use super::web_search::WebSearchItem;
fn completed_image_generation_item() -> ExtensionItem {
ExtensionItem::ImageGeneration(ImageGenerationItem {
@@ -35,6 +37,37 @@ fn image_generation_item_preserves_stable_wire_shape() {
);
}
#[test]
fn web_search_item_preserves_stable_wire_shape() {
let item = ExtensionItem::WebSearch(WebSearchItem {
id: "search-1".to_string(),
query: "docs".to_string(),
action: Some(WebSearchAction::Search {
query: Some("docs".to_string()),
queries: None,
}),
});
let value = serde_json::to_value(&item).expect("serialize extension item");
assert_eq!(
value,
json!({
"kind": "web.search",
"id": "search-1",
"query": "docs",
"action": {
"type": "search",
"query": "docs",
"queries": null,
},
})
);
assert_eq!(
serde_json::from_value::<ExtensionItem>(value).expect("deserialize extension item"),
item
);
}
#[test]
fn unknown_extension_kind_is_rejected() {
let value = json!({