[codex] Enable standalone web search in code mode (#26719)

## What

- Consume plaintext `output` from standalone search while retaining
optional `encrypted_output` parsing.
- Expose `web.run` to code mode and return search output to nested
JavaScript calls.
- Cover direct and code-mode standalone search paths with integration
tests.

## Why

`/v1/alpha/search` now returns plaintext output, which code mode needs
to consume standalone search results.

## Test plan

- `just test -p codex-api`
- `just test -p codex-web-search-extension`
- `just test -p codex-core code_mode_can_call_standalone_web_search`
- `just test -p codex-app-server
standalone_web_search_round_trips_output`
This commit is contained in:
rka-oai
2026-06-07 23:18:23 -07:00
committed by GitHub
parent 5a440c03f2
commit ed6e5cf919
6 changed files with 141 additions and 27 deletions
+14 -14
View File
@@ -4,19 +4,19 @@ use codex_protocol::models::FunctionCallOutputContentItem;
use codex_protocol::models::FunctionCallOutputPayload;
use codex_protocol::models::ResponseInputItem;
pub(crate) struct EncryptedSearchOutput {
encrypted_output: String,
pub(crate) struct SearchOutput {
output: String,
}
impl EncryptedSearchOutput {
pub(crate) fn new(encrypted_output: String) -> Self {
Self { encrypted_output }
impl SearchOutput {
pub(crate) fn new(output: String) -> Self {
Self { output }
}
}
impl ToolOutput for EncryptedSearchOutput {
impl ToolOutput for SearchOutput {
fn log_preview(&self) -> String {
"[encrypted standalone web search output]".to_string()
"[standalone web search output]".to_string()
}
fn success_for_logging(&self) -> bool {
@@ -29,8 +29,8 @@ impl ToolOutput for EncryptedSearchOutput {
ResponseInputItem::FunctionCallOutput {
call_id: call_id.to_string(),
output: FunctionCallOutputPayload::from_content_items(vec![
FunctionCallOutputContentItem::EncryptedContent {
encrypted_content: self.encrypted_output.clone(),
FunctionCallOutputContentItem::InputText {
text: self.output.clone(),
},
]),
}
@@ -45,12 +45,12 @@ mod tests {
use codex_protocol::models::ResponseInputItem;
use pretty_assertions::assert_eq;
use super::EncryptedSearchOutput;
use super::SearchOutput;
use super::ToolOutput;
#[test]
fn emits_encrypted_function_call_output() {
let output = EncryptedSearchOutput::new("encrypted-search-output".to_string());
fn emits_plaintext_function_call_output() {
let output = SearchOutput::new("search output".to_string());
assert_eq!(
output.to_response_item(
@@ -62,8 +62,8 @@ mod tests {
ResponseInputItem::FunctionCallOutput {
call_id: "call-1".to_string(),
output: FunctionCallOutputPayload::from_content_items(vec![
FunctionCallOutputContentItem::EncryptedContent {
encrypted_content: "encrypted-search-output".to_string(),
FunctionCallOutputContentItem::InputText {
text: "search output".to_string(),
},
]),
}
+3 -5
View File
@@ -26,7 +26,7 @@ use http::HeaderMap;
use url::Url;
use crate::history::recent_input;
use crate::output::EncryptedSearchOutput;
use crate::output::SearchOutput;
use crate::schema::commands_schema;
pub(crate) const WEB_NAMESPACE: &str = "web";
@@ -67,7 +67,7 @@ impl ToolExecutor<ToolCall> for WebSearchTool {
}
fn exposure(&self) -> ToolExposure {
ToolExposure::DirectModelOnly
ToolExposure::Direct
}
fn supports_parallel_tool_calls(&self) -> bool {
@@ -114,9 +114,7 @@ impl ToolExecutor<ToolCall> for WebSearchTool {
.emit_completed(web_search_item(&call.call_id, command_action))
.await;
Ok(Box::new(EncryptedSearchOutput::new(
response.encrypted_output,
)))
Ok(Box::new(SearchOutput::new(response.output)))
}
}