standalone websearch extension (#23823)

## Summary

Add the extension-backed standalone `web.run` tool so Codex can call the
standalone search endpoint through the `codex-api` search client and
return its encrypted output to Responses.

- gate the new tool behind `standalone_web_search`
- install the extension in the app-server thread registry and hide
hosted `web_search` when standalone search is enabled for OpenAI
providers so the two paths stay mutually exclusive
- build search context from persisted history using a small tail
heuristic: previous user message, assistant text between the last two
user turns capped at about 1k tokens, and current user message

## Test Plan

- `cargo test -p codex-web-search-extension`
- `cargo test -p codex-api`
- `cargo test -p codex-core
hosted_tools_follow_provider_auth_model_and_config_gates`
This commit is contained in:
sayan-oai
2026-05-26 11:12:24 -07:00
committed by GitHub
parent aad59a0916
commit a22706dfae
26 changed files with 1238 additions and 22 deletions
+14 -4
View File
@@ -238,11 +238,12 @@ fn spec_for_model_request(
}
}
pub(crate) fn hosted_model_tool_specs(turn_context: &TurnContext) -> Vec<ToolSpec> {
fn hosted_model_tool_specs(context: &CoreToolPlanContext<'_>) -> Vec<ToolSpec> {
let turn_context = context.turn_context;
let mut specs = Vec::new();
let provider_capabilities = turn_context.provider.capabilities();
let web_search_mode = provider_capabilities
.web_search
let web_search_mode = (!standalone_web_run_available(context.extension_tool_executors)
&& provider_capabilities.web_search)
.then_some(turn_context.config.web_search_mode.value());
let web_search_config = if provider_capabilities.web_search {
turn_context.config.web_search_config.as_ref()
@@ -504,11 +505,20 @@ fn add_tool_sources(context: &CoreToolPlanContext<'_>, planned_tools: &mut Plann
add_mcp_runtime_tools(context, planned_tools);
add_dynamic_tools(context, planned_tools);
add_extension_tools(context, planned_tools);
for spec in hosted_model_tool_specs(context.turn_context) {
for spec in hosted_model_tool_specs(context) {
planned_tools.add_hosted_spec(spec);
}
}
fn standalone_web_run_available(
extension_tools: &[Arc<dyn ToolExecutor<ExtensionToolCall>>],
) -> bool {
let web_run = ToolName::namespaced("web", "run");
extension_tools
.iter()
.any(|executor| executor.tool_name() == web_run)
}
fn add_shell_tools(context: &CoreToolPlanContext<'_>, planned_tools: &mut PlannedTools) {
let turn_context = context.turn_context;
let features = turn_context.features.get();