fix: enable per-turn updates to web search mode (#10040)

web_search can now be updated per-turn, for things like changes to
sandbox policy.

`SandboxPolicy::DangerFullAccess` now sets web_search to `live`, and the
default is still `cached`.

Added integration tests.
This commit is contained in:
sayan-oai 2026-01-27 18:09:29 -08:00 committed by GitHub
parent 3f3916e595
commit a90ab789c2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 187 additions and 102 deletions

View file

@ -655,11 +655,13 @@ fn build_responses_headers(
let mut headers = experimental_feature_headers(config);
headers.insert(
WEB_SEARCH_ELIGIBLE_HEADER,
HeaderValue::from_static(if config.web_search_mode == WebSearchMode::Disabled {
"false"
} else {
"true"
}),
HeaderValue::from_static(
if matches!(config.web_search_mode, Some(WebSearchMode::Disabled)) {
"false"
} else {
"true"
},
),
);
if let Some(turn_state) = turn_state
&& let Some(state) = turn_state.get()

View file

@ -100,6 +100,7 @@ use crate::config::Config;
use crate::config::Constrained;
use crate::config::ConstraintResult;
use crate::config::GhostSnapshotConfig;
use crate::config::resolve_web_search_mode_for_turn;
use crate::config::types::McpServerConfig;
use crate::config::types::ShellEnvironmentPolicy;
use crate::context_manager::ContextManager;
@ -581,6 +582,10 @@ impl Session {
session_configuration.collaboration_mode.reasoning_effort();
per_turn_config.model_reasoning_summary = session_configuration.model_reasoning_summary;
per_turn_config.model_personality = session_configuration.personality;
per_turn_config.web_search_mode = Some(resolve_web_search_mode_for_turn(
per_turn_config.web_search_mode,
session_configuration.sandbox_policy.get(),
));
per_turn_config.features = config.features.clone();
per_turn_config
}
@ -2888,7 +2893,7 @@ async fn spawn_review_thread(
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &review_model_info,
features: &review_features,
web_search_mode: review_web_search_mode,
web_search_mode: Some(review_web_search_mode),
});
let review_prompt = resolved.prompt.clone();
@ -2900,7 +2905,7 @@ async fn spawn_review_thread(
let mut per_turn_config = (*config).clone();
per_turn_config.model = Some(model.clone());
per_turn_config.features = review_features.clone();
per_turn_config.web_search_mode = review_web_search_mode;
per_turn_config.web_search_mode = Some(review_web_search_mode);
let otel_manager = parent_turn_context
.client

View file

@ -306,8 +306,8 @@ pub struct Config {
/// model info's default preference.
pub include_apply_patch_tool: bool,
/// Explicit or feature-derived web search mode. Defaults to cached.
pub web_search_mode: WebSearchMode,
/// Explicit or feature-derived web search mode.
pub web_search_mode: Option<WebSearchMode>,
/// If set to `true`, used only the experimental unified exec tool.
pub use_experimental_unified_exec_tool: bool,
@ -1203,27 +1203,36 @@ pub fn resolve_oss_provider(
}
}
/// Resolve the web search mode from explicit config, feature flags, and sandbox policy.
/// Live search is auto-enabled when sandbox policy is `DangerFullAccess`
/// Resolve the web search mode from explicit config and feature flags.
fn resolve_web_search_mode(
config_toml: &ConfigToml,
config_profile: &ConfigProfile,
features: &Features,
sandbox_policy: &SandboxPolicy,
) -> WebSearchMode {
) -> Option<WebSearchMode> {
if let Some(mode) = config_profile.web_search.or(config_toml.web_search) {
return mode;
return Some(mode);
}
if features.enabled(Feature::WebSearchCached) {
return WebSearchMode::Cached;
return Some(WebSearchMode::Cached);
}
if features.enabled(Feature::WebSearchRequest) {
return WebSearchMode::Live;
return Some(WebSearchMode::Live);
}
None
}
pub(crate) fn resolve_web_search_mode_for_turn(
explicit_mode: Option<WebSearchMode>,
sandbox_policy: &SandboxPolicy,
) -> WebSearchMode {
if let Some(mode) = explicit_mode {
return mode;
}
if matches!(sandbox_policy, SandboxPolicy::DangerFullAccess) {
return WebSearchMode::Live;
WebSearchMode::Live
} else {
WebSearchMode::Cached
}
WebSearchMode::Cached
}
impl Config {
@ -1347,8 +1356,7 @@ impl Config {
AskForApproval::default()
}
});
let web_search_mode =
resolve_web_search_mode(&cfg, &config_profile, &features, &sandbox_policy);
let web_search_mode = resolve_web_search_mode(&cfg, &config_profile, &features);
// TODO(dylan): We should be able to leverage ConfigLayerStack so that
// we can reliably check this at every config level.
let did_user_set_custom_approval_policy_or_sandbox_mode = approval_policy_override
@ -2271,15 +2279,12 @@ trust_level = "trusted"
}
#[test]
fn web_search_mode_defaults_to_cached_if_unset() {
fn web_search_mode_defaults_to_none_if_unset() {
let cfg = ConfigToml::default();
let profile = ConfigProfile::default();
let features = Features::with_defaults();
assert_eq!(
resolve_web_search_mode(&cfg, &profile, &features, &SandboxPolicy::ReadOnly),
WebSearchMode::Cached
);
assert_eq!(resolve_web_search_mode(&cfg, &profile, &features), None);
}
#[test]
@ -2293,8 +2298,8 @@ trust_level = "trusted"
features.enable(Feature::WebSearchCached);
assert_eq!(
resolve_web_search_mode(&cfg, &profile, &features, &SandboxPolicy::ReadOnly),
WebSearchMode::Live
resolve_web_search_mode(&cfg, &profile, &features),
Some(WebSearchMode::Live)
);
}
@ -2309,48 +2314,33 @@ trust_level = "trusted"
features.enable(Feature::WebSearchRequest);
assert_eq!(
resolve_web_search_mode(&cfg, &profile, &features, &SandboxPolicy::ReadOnly),
WebSearchMode::Disabled
resolve_web_search_mode(&cfg, &profile, &features),
Some(WebSearchMode::Disabled)
);
}
#[test]
fn danger_full_access_defaults_web_search_live_when_unset() -> std::io::Result<()> {
let codex_home = TempDir::new()?;
let cfg = ConfigToml {
sandbox_mode: Some(SandboxMode::DangerFullAccess),
..Default::default()
};
fn web_search_mode_for_turn_defaults_to_cached_when_unset() {
let mode = resolve_web_search_mode_for_turn(None, &SandboxPolicy::ReadOnly);
let config = Config::load_from_base_config_with_overrides(
cfg,
ConfigOverrides::default(),
codex_home.path().to_path_buf(),
)?;
assert_eq!(config.web_search_mode, WebSearchMode::Live);
Ok(())
assert_eq!(mode, WebSearchMode::Cached);
}
#[test]
fn explicit_web_search_mode_wins_in_danger_full_access() -> std::io::Result<()> {
let codex_home = TempDir::new()?;
let cfg = ConfigToml {
sandbox_mode: Some(SandboxMode::DangerFullAccess),
web_search: Some(WebSearchMode::Cached),
..Default::default()
};
fn web_search_mode_for_turn_defaults_to_live_for_danger_full_access() {
let mode = resolve_web_search_mode_for_turn(None, &SandboxPolicy::DangerFullAccess);
let config = Config::load_from_base_config_with_overrides(
cfg,
ConfigOverrides::default(),
codex_home.path().to_path_buf(),
)?;
assert_eq!(mode, WebSearchMode::Live);
}
assert_eq!(config.web_search_mode, WebSearchMode::Cached);
#[test]
fn web_search_mode_for_turn_prefers_explicit_value() {
let mode = resolve_web_search_mode_for_turn(
Some(WebSearchMode::Cached),
&SandboxPolicy::DangerFullAccess,
);
Ok(())
assert_eq!(mode, WebSearchMode::Cached);
}
#[test]
@ -3786,7 +3776,7 @@ model_verbosity = "high"
forced_chatgpt_workspace_id: None,
forced_login_method: None,
include_apply_patch_tool: false,
web_search_mode: WebSearchMode::Cached,
web_search_mode: None,
use_experimental_unified_exec_tool: false,
ghost_snapshot: GhostSnapshotConfig::default(),
features: Features::with_defaults(),
@ -3869,7 +3859,7 @@ model_verbosity = "high"
forced_chatgpt_workspace_id: None,
forced_login_method: None,
include_apply_patch_tool: false,
web_search_mode: WebSearchMode::Cached,
web_search_mode: None,
use_experimental_unified_exec_tool: false,
ghost_snapshot: GhostSnapshotConfig::default(),
features: Features::with_defaults(),
@ -3967,7 +3957,7 @@ model_verbosity = "high"
forced_chatgpt_workspace_id: None,
forced_login_method: None,
include_apply_patch_tool: false,
web_search_mode: WebSearchMode::Cached,
web_search_mode: None,
use_experimental_unified_exec_tool: false,
ghost_snapshot: GhostSnapshotConfig::default(),
features: Features::with_defaults(),
@ -4051,7 +4041,7 @@ model_verbosity = "high"
forced_chatgpt_workspace_id: None,
forced_login_method: None,
include_apply_patch_tool: false,
web_search_mode: WebSearchMode::Cached,
web_search_mode: None,
use_experimental_unified_exec_tool: false,
ghost_snapshot: GhostSnapshotConfig::default(),
features: Features::with_defaults(),

View file

@ -86,7 +86,7 @@ async fn start_review_conversation(
let mut sub_agent_config = config.as_ref().clone();
// Carry over review-only feature restrictions so the delegate cannot
// re-enable blocked tools (web search, view image).
sub_agent_config.web_search_mode = WebSearchMode::Disabled;
sub_agent_config.web_search_mode = Some(WebSearchMode::Disabled);
// Set explicit review rubric for the sub-agent
sub_agent_config.base_instructions = Some(crate::REVIEW_PROMPT.to_string());

View file

@ -27,7 +27,7 @@ use std::collections::HashMap;
pub(crate) struct ToolsConfig {
pub shell_type: ConfigShellToolType,
pub apply_patch_tool_type: Option<ApplyPatchToolType>,
pub web_search_mode: WebSearchMode,
pub web_search_mode: Option<WebSearchMode>,
pub collab_tools: bool,
pub collaboration_modes_tools: bool,
pub experimental_supported_tools: Vec<String>,
@ -36,7 +36,7 @@ pub(crate) struct ToolsConfig {
pub(crate) struct ToolsConfigParams<'a> {
pub(crate) model_info: &'a ModelInfo,
pub(crate) features: &'a Features,
pub(crate) web_search_mode: WebSearchMode,
pub(crate) web_search_mode: Option<WebSearchMode>,
}
impl ToolsConfig {
@ -1384,17 +1384,17 @@ pub(crate) fn build_specs(
}
match config.web_search_mode {
WebSearchMode::Cached => {
Some(WebSearchMode::Cached) => {
builder.push_spec(ToolSpec::WebSearch {
external_web_access: Some(false),
});
}
WebSearchMode::Live => {
Some(WebSearchMode::Live) => {
builder.push_spec(ToolSpec::WebSearch {
external_web_access: Some(true),
});
}
WebSearchMode::Disabled => {}
Some(WebSearchMode::Disabled) | None => {}
}
builder.push_spec_with_parallel_support(create_view_image_tool(), true);
@ -1556,7 +1556,7 @@ mod tests {
let config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Live,
web_search_mode: Some(WebSearchMode::Live),
});
let (tools, _) = build_specs(&config, None, &[]).build();
@ -1620,7 +1620,7 @@ mod tests {
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
web_search_mode: Some(WebSearchMode::Cached),
});
let (tools, _) = build_specs(&tools_config, None, &[]).build();
assert_contains_tool_names(
@ -1638,7 +1638,7 @@ mod tests {
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
web_search_mode: Some(WebSearchMode::Cached),
});
let (tools, _) = build_specs(&tools_config, None, &[]).build();
assert!(
@ -1650,7 +1650,7 @@ mod tests {
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
web_search_mode: Some(WebSearchMode::Cached),
});
let (tools, _) = build_specs(&tools_config, None, &[]).build();
assert_contains_tool_names(&tools, &["request_user_input"]);
@ -1659,7 +1659,7 @@ mod tests {
fn assert_model_tools(
model_slug: &str,
features: &Features,
web_search_mode: WebSearchMode,
web_search_mode: Option<WebSearchMode>,
expected_tools: &[&str],
) {
let config = test_config();
@ -1683,7 +1683,7 @@ mod tests {
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
web_search_mode: Some(WebSearchMode::Cached),
});
let (tools, _) = build_specs(&tools_config, None, &[]).build();
@ -1705,7 +1705,7 @@ mod tests {
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Live,
web_search_mode: Some(WebSearchMode::Live),
});
let (tools, _) = build_specs(&tools_config, None, &[]).build();
@ -1725,7 +1725,7 @@ mod tests {
assert_model_tools(
"gpt-5-codex",
&features,
WebSearchMode::Cached,
Some(WebSearchMode::Cached),
&[
"shell_command",
"list_mcp_resources",
@ -1747,7 +1747,7 @@ mod tests {
assert_model_tools(
"gpt-5.1-codex",
&features,
WebSearchMode::Cached,
Some(WebSearchMode::Cached),
&[
"shell_command",
"list_mcp_resources",
@ -1770,7 +1770,7 @@ mod tests {
assert_model_tools(
"gpt-5-codex",
&features,
WebSearchMode::Live,
Some(WebSearchMode::Live),
&[
"exec_command",
"write_stdin",
@ -1794,7 +1794,7 @@ mod tests {
assert_model_tools(
"gpt-5.1-codex",
&features,
WebSearchMode::Live,
Some(WebSearchMode::Live),
&[
"exec_command",
"write_stdin",
@ -1817,7 +1817,7 @@ mod tests {
assert_model_tools(
"codex-mini-latest",
&features,
WebSearchMode::Cached,
Some(WebSearchMode::Cached),
&[
"local_shell",
"list_mcp_resources",
@ -1838,7 +1838,7 @@ mod tests {
assert_model_tools(
"gpt-5.1-codex-mini",
&features,
WebSearchMode::Cached,
Some(WebSearchMode::Cached),
&[
"shell_command",
"list_mcp_resources",
@ -1860,7 +1860,7 @@ mod tests {
assert_model_tools(
"gpt-5",
&features,
WebSearchMode::Cached,
Some(WebSearchMode::Cached),
&[
"shell",
"list_mcp_resources",
@ -1881,7 +1881,7 @@ mod tests {
assert_model_tools(
"gpt-5.1",
&features,
WebSearchMode::Cached,
Some(WebSearchMode::Cached),
&[
"shell_command",
"list_mcp_resources",
@ -1903,7 +1903,7 @@ mod tests {
assert_model_tools(
"exp-5.1",
&features,
WebSearchMode::Cached,
Some(WebSearchMode::Cached),
&[
"exec_command",
"write_stdin",
@ -1927,7 +1927,7 @@ mod tests {
assert_model_tools(
"codex-mini-latest",
&features,
WebSearchMode::Live,
Some(WebSearchMode::Live),
&[
"exec_command",
"write_stdin",
@ -1951,7 +1951,7 @@ mod tests {
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Live,
web_search_mode: Some(WebSearchMode::Live),
});
let (tools, _) = build_specs(&tools_config, Some(HashMap::new()), &[]).build();
@ -1973,7 +1973,7 @@ mod tests {
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
web_search_mode: Some(WebSearchMode::Cached),
});
let (tools, _) = build_specs(&tools_config, None, &[]).build();
@ -1992,7 +1992,7 @@ mod tests {
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
web_search_mode: Some(WebSearchMode::Cached),
});
let (tools, _) = build_specs(&tools_config, None, &[]).build();
@ -2023,7 +2023,7 @@ mod tests {
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Live,
web_search_mode: Some(WebSearchMode::Live),
});
let (tools, _) = build_specs(
&tools_config,
@ -2119,7 +2119,7 @@ mod tests {
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
web_search_mode: Some(WebSearchMode::Cached),
});
// Intentionally construct a map with keys that would sort alphabetically.
@ -2196,7 +2196,7 @@ mod tests {
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
web_search_mode: Some(WebSearchMode::Cached),
});
let (tools, _) = build_specs(
@ -2254,7 +2254,7 @@ mod tests {
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
web_search_mode: Some(WebSearchMode::Cached),
});
let (tools, _) = build_specs(
@ -2309,7 +2309,7 @@ mod tests {
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
web_search_mode: Some(WebSearchMode::Cached),
});
let (tools, _) = build_specs(
@ -2366,7 +2366,7 @@ mod tests {
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
web_search_mode: Some(WebSearchMode::Cached),
});
let (tools, _) = build_specs(
@ -2479,7 +2479,7 @@ Examples of valid command strings:
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: WebSearchMode::Cached,
web_search_mode: Some(WebSearchMode::Cached),
});
let (tools, _) = build_specs(
&tools_config,

View file

@ -264,7 +264,7 @@ async fn responses_stream_includes_web_search_eligible_header_false_when_disable
let test = test_codex()
.with_config(|config| {
config.web_search_mode = WebSearchMode::Disabled;
config.web_search_mode = Some(WebSearchMode::Disabled);
})
.build(&server)
.await

View file

@ -38,7 +38,7 @@ async fn collect_tool_identifiers_for_model(model: &str) -> Vec<String> {
.with_model(model)
// Keep tool expectations stable when the default web_search mode changes.
.with_config(|config| {
config.web_search_mode = WebSearchMode::Cached;
config.web_search_mode = Some(WebSearchMode::Cached);
config.features.enable(Feature::CollaborationModes);
});
let test = builder

View file

@ -92,7 +92,7 @@ async fn prompt_tools_are_consistent_across_requests() -> anyhow::Result<()> {
config.user_instructions = Some("be consistent and helpful".to_string());
config.model = Some("gpt-5.1-codex-max".to_string());
// Keep tool expectations stable when the default web_search mode changes.
config.web_search_mode = WebSearchMode::Cached;
config.web_search_mode = Some(WebSearchMode::Cached);
config.features.enable(Feature::CollaborationModes);
})
.build(&server)

View file

@ -1,6 +1,7 @@
#![allow(clippy::unwrap_used)]
use codex_core::features::Feature;
use codex_core::protocol::SandboxPolicy;
use codex_protocol::config_types::WebSearchMode;
use core_test_support::load_sse_fixture_with_id;
use core_test_support::responses;
@ -25,7 +26,7 @@ fn find_web_search_tool(body: &Value) -> &Value {
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn web_search_mode_cached_sets_external_web_access_false_in_request_body() {
async fn web_search_mode_cached_sets_external_web_access_false() {
skip_if_no_network!();
let server = start_mock_server().await;
@ -35,7 +36,7 @@ async fn web_search_mode_cached_sets_external_web_access_false_in_request_body()
let mut builder = test_codex()
.with_model("gpt-5-codex")
.with_config(|config| {
config.web_search_mode = WebSearchMode::Cached;
config.web_search_mode = Some(WebSearchMode::Cached);
});
let test = builder
.build(&server)
@ -56,7 +57,7 @@ async fn web_search_mode_cached_sets_external_web_access_false_in_request_body()
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn web_search_mode_takes_precedence_over_legacy_flags_in_request_body() {
async fn web_search_mode_takes_precedence_over_legacy_flags() {
skip_if_no_network!();
let server = start_mock_server().await;
@ -67,7 +68,7 @@ async fn web_search_mode_takes_precedence_over_legacy_flags_in_request_body() {
.with_model("gpt-5-codex")
.with_config(|config| {
config.features.enable(Feature::WebSearchRequest);
config.web_search_mode = WebSearchMode::Cached;
config.web_search_mode = Some(WebSearchMode::Cached);
});
let test = builder
.build(&server)
@ -86,3 +87,90 @@ async fn web_search_mode_takes_precedence_over_legacy_flags_in_request_body() {
"web_search mode should win over legacy web_search_request"
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn web_search_mode_defaults_to_cached_when_unset() {
skip_if_no_network!();
let server = start_mock_server().await;
let sse = sse_completed("resp-1");
let resp_mock = responses::mount_sse_once(&server, sse).await;
let mut builder = test_codex()
.with_model("gpt-5-codex")
.with_config(|config| {
config.web_search_mode = None;
config.features.disable(Feature::WebSearchCached);
config.features.disable(Feature::WebSearchRequest);
});
let test = builder
.build(&server)
.await
.expect("create test Codex conversation");
test.submit_turn_with_policy("hello default cached web search", SandboxPolicy::ReadOnly)
.await
.expect("submit turn");
let body = resp_mock.single_request().body_json();
let tool = find_web_search_tool(&body);
assert_eq!(
tool.get("external_web_access").and_then(Value::as_bool),
Some(false),
"default web_search should be cached when unset"
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn web_search_mode_updates_between_turns_with_sandbox_policy() {
skip_if_no_network!();
let server = start_mock_server().await;
let resp_mock = responses::mount_sse_sequence(
&server,
vec![sse_completed("resp-1"), sse_completed("resp-2")],
)
.await;
let mut builder = test_codex()
.with_model("gpt-5-codex")
.with_config(|config| {
config.web_search_mode = None;
config.features.disable(Feature::WebSearchCached);
config.features.disable(Feature::WebSearchRequest);
});
let test = builder
.build(&server)
.await
.expect("create test Codex conversation");
test.submit_turn_with_policy("hello cached", SandboxPolicy::ReadOnly)
.await
.expect("submit first turn");
test.submit_turn_with_policy("hello live", SandboxPolicy::DangerFullAccess)
.await
.expect("submit second turn");
let requests = resp_mock.requests();
assert_eq!(requests.len(), 2, "expected two response requests");
let first_body = requests[0].body_json();
let first_tool = find_web_search_tool(&first_body);
assert_eq!(
first_tool
.get("external_web_access")
.and_then(Value::as_bool),
Some(false),
"read-only policy should default web_search to cached"
);
let second_body = requests[1].body_json();
let second_tool = find_web_search_tool(&second_body);
assert_eq!(
second_tool
.get("external_web_access")
.and_then(Value::as_bool),
Some(true),
"danger-full-access policy should default web_search to live"
);
}