Add indexed web search mode (#28489)

## Summary

- Add `web_search = "indexed"` alongside `disabled`, `cached`, and
`live`.
- Use that same resolved mode for both hosted and standalone web search.
- For hosted search, send `index_gated_web_access: true` with external
web access enabled only when `indexed` is selected.
- For standalone search, preserve the existing boolean wire values for
existing modes (`cached` maps to `false` and `live` to `true`) and send
`"indexed"` only for `indexed`; `disabled` keeps the tool unavailable.
- Carry the mode through managed configuration requirements and
generated schemas.

## Why

Indexed search provides a middle ground between cached-only search and
unrestricted live page fetching. Search queries can remain live while
direct page fetches are limited to URLs admitted by the server.

The existing `web_search` setting remains the single source of truth, so
hosted and standalone executors cannot drift into different access
modes. Without an explicit `indexed` selection, the existing
model-visible tool and request shapes are unchanged.

```toml
web_search = "indexed"

[features]
standalone_web_search = true
```

## Validation

- `just fmt`
- `just test -p codex-api` (`126 passed`)
- `just test -p codex-web-search-extension` (`7 passed`)
- `just test -p codex-core
code_mode_can_call_indexed_standalone_web_search` (`1 passed`)
- Focused configuration, hosted request, standalone request, and
managed-requirement coverage is included in the PR; remaining suites run
in CI.

The full workspace test suite was not run locally.
This commit is contained in:
Winston Howes
2026-06-19 05:35:57 -07:00
committed by GitHub
parent 7951397e4a
commit 3a2712ea14
24 changed files with 204 additions and 23 deletions
+7 -5
View File
@@ -18,11 +18,12 @@ pub fn create_image_generation_tool(output_format: &str) -> ToolSpec {
}
pub fn create_web_search_tool(options: WebSearchToolOptions<'_>) -> Option<ToolSpec> {
let external_web_access = match options.web_search_mode {
Some(WebSearchMode::Cached) => Some(false),
Some(WebSearchMode::Live) => Some(true),
Some(WebSearchMode::Disabled) | None => None,
}?;
let (external_web_access, index_gated_web_access) = match options.web_search_mode {
Some(WebSearchMode::Cached) => (false, None),
Some(WebSearchMode::Indexed) => (true, Some(true)),
Some(WebSearchMode::Live) => (true, None),
Some(WebSearchMode::Disabled) | None => return None,
};
let search_content_types = match options.web_search_tool_type {
WebSearchToolType::Text => None,
@@ -36,6 +37,7 @@ pub fn create_web_search_tool(options: WebSearchToolOptions<'_>) -> Option<ToolS
Some(ToolSpec::WebSearch {
external_web_access: Some(external_web_access),
index_gated_web_access,
filters: options
.web_search_config
.and_then(|config| config.filters.clone().map(Into::into)),
@@ -39,6 +39,7 @@ fn web_search_tool_preserves_configured_options() {
}),
Some(ToolSpec::WebSearch {
external_web_access: Some(true),
index_gated_web_access: None,
filters: Some(ResponsesApiWebSearchFilters {
allowed_domains: Some(vec!["example.com".to_string()]),
}),
@@ -1441,6 +1441,7 @@ async fn hosted_tools_follow_provider_auth_model_and_config_gates() {
live_web_search.visible_spec("web_search"),
&ToolSpec::WebSearch {
external_web_access: Some(true),
index_gated_web_access: None,
filters: None,
user_location: None,
search_context_size: None,