Add ranked tool discovery to JavaScript code mode (#51209)

## What changed

- Add the default-disabled `code_mode_tool_search` feature to expose `await tools.tool_search({query: "...", limit: 8})` in code mode when the model supports tool search.
- Return BM25-ranked deferred tools as `{tools: [{name, description}]}`, including callable names and TypeScript declarations for use in later code cells.
- Filter results through the current step's nested-tool policy before applying the limit, preserving namespace restrictions, normalized identities, description prefixes, and schema budgets.
- Add ranked-search guidance while retaining `ALL_TOOLS` catalog discovery and description overrides.

## Testing

Add coverage for feature and model-capability gating, namespace restrictions, argument validation, schema budgets, and discovery followed by deferred MCP and dynamic tool calls. Extend the scenario snapshot to cover search, invocation, yielding, and completion through `wait`.

GitOrigin-RevId: 092a29dd5f0773c518f53a001b9cafb917422f13
This commit is contained in:
Alex Zamoshchin
2026-10-06 00:36:25 +00:00
committed by copyberry
parent 5ad6891696
commit 4d15794336
23 changed files with 1125 additions and 248 deletions
@@ -2,9 +2,11 @@
use super::CodeModeToolKind;
use super::DEFERRED_NESTED_TOOLS_GUIDANCE;
use super::DeferredToolDiscovery;
use super::ImageDetailVisibility;
use super::LEGACY_IMAGE_HELPER_DESCRIPTION;
use super::MCP_TYPESCRIPT_PREAMBLE;
use super::TOOL_SEARCH_GUIDANCE;
use super::ToolDefinition;
use super::UNIFIED_IMAGE_HELPER_DESCRIPTION;
use super::build_exec_tool_description;
@@ -34,6 +36,7 @@ fn exec_override_renders_only_known_literal_placeholders() {
/*default_exec_yield_time_ms*/ 4567,
/*code_mode_only*/ false,
image_detail_visibility,
DeferredToolDiscovery::Catalog,
Some(&CodeModeToolMessages {
exec: Some(ToolMessage {
description: Some(" \nDefaults to 10000 ms. {{ default_exec_yield_time_ms }} ms.\n{{ image_helper }}\n{{ unknown }} {{default_exec_yield_time_ms}}\t ".to_string()),
@@ -63,6 +66,7 @@ fn exec_override_preserves_empty_and_whitespace_only_text() {
crate::DEFAULT_EXEC_YIELD_TIME_MS,
/*code_mode_only*/ true,
ImageDetailVisibility::Visible,
DeferredToolDiscovery::Catalog,
Some(&CodeModeToolMessages {
exec: Some(ToolMessage {
description: Some(description_override.to_string()),
@@ -153,6 +157,7 @@ fn exec_override_preserves_runtime_sections() {
crate::DEFAULT_EXEC_YIELD_TIME_MS,
code_mode_only,
ImageDetailVisibility::Visible,
DeferredToolDiscovery::Catalog,
Some(&CodeModeToolMessages {
exec: Some(ToolMessage {
description: Some(String::new()),
@@ -168,6 +173,53 @@ fn exec_override_preserves_runtime_sections() {
}
}
#[test]
fn ranked_search_guidance_honors_overrides() {
let deferred_tools = [ToolDefinition {
name: "deferred_tool".to_string(),
tool_name: ToolName::plain("deferred_tool"),
description: "Deferred tool".to_string(),
kind: CodeModeToolKind::Function,
input_schema: None,
input_schema_max_bytes: None,
output_schema: None,
}];
for code_mode_only in [false, true] {
for (guidance, expected) in [
(
None,
if code_mode_only {
DEFERRED_NESTED_TOOLS_GUIDANCE.to_string()
} else {
format!("{DEFERRED_NESTED_TOOLS_GUIDANCE}\n\n{TOOL_SEARCH_GUIDANCE}")
},
),
(Some(""), String::new()),
(Some("Catalog discovery."), "Catalog discovery.".to_string()),
] {
let description = build_exec_tool_description(
&[],
&deferred_tools,
&BTreeMap::new(),
crate::DEFAULT_EXEC_YIELD_TIME_MS,
code_mode_only,
ImageDetailVisibility::Visible,
DeferredToolDiscovery::RankedSearch,
Some(&CodeModeToolMessages {
exec: Some(ToolMessage {
description: Some(String::new()),
..Default::default()
}),
deferred_nested_tools_guidance: guidance.map(str::to_string),
mcp_typescript_preamble: Some(String::new()),
..Default::default()
}),
);
assert_eq!(description, expected);
}
}
}
#[test]
fn mcp_types_stay_stable_when_a_deferred_tool_changes_its_output_schema() {
let mut tool = ToolDefinition {
@@ -187,6 +239,7 @@ fn mcp_types_stay_stable_when_a_deferred_tool_changes_its_output_schema() {
crate::DEFAULT_EXEC_YIELD_TIME_MS,
/*code_mode_only*/ true,
ImageDetailVisibility::Visible,
DeferredToolDiscovery::Catalog,
Some(&CodeModeToolMessages {
mcp_typescript_preamble: preamble.map(str::to_string),
..Default::default()
@@ -229,6 +282,7 @@ fn nested_guidance_survives_catalog_changes_and_respects_overrides() {
crate::DEFAULT_EXEC_YIELD_TIME_MS,
code_mode_only,
ImageDetailVisibility::Visible,
DeferredToolDiscovery::Catalog,
Some(&CodeModeToolMessages {
exec: Some(ToolMessage {
description: Some(String::new()),