Allow hiding native tools while retaining the multi-agent V2 runtime (#53617)

## What changed

Add `features.multi_agent_v2.disable_native_tools` to hide native multi-agent tools while keeping the V2 runtime and its instructions enabled for host-provided tools. The option defaults to `false`, preserving existing tool exposure.

## Testing

Extend configuration parsing tests for the new option and parameterize the multi-agent catalog scenario to cover both settings. Add a snapshot showing native tools are absent while the V2 runtime and instructions remain enabled.

GitOrigin-RevId: 21a53584d2395bb678813c876a8b7927bea61c90
This commit is contained in:
jif
2026-10-13 09:06:39 +00:00
committed by copyberry
parent 1a69ad3237
commit 308ddf5e7c
8 changed files with 142 additions and 7 deletions
+4
View File
@@ -2947,6 +2947,10 @@
"description": "Disable the model's direct-message tools; spawning and automatic child results remain available.",
"type": "boolean"
},
"disable_native_tools": {
"description": "Hide native multi-agent tools while retaining the V2 runtime for host-provided tools.",
"type": "boolean"
},
"enabled": {
"type": "boolean"
},
+2
View File
@@ -12216,6 +12216,7 @@ tool_namespace = "agents"
hide_spawn_agent_metadata = true
expose_spawn_agent_model_overrides = false
wait_agent_enabled = false
disable_native_tools = true
disable_direct_message = true
message_board_in_memory = true
non_code_mode_only = true
@@ -12274,6 +12275,7 @@ max_concurrent_threads_per_session = 9
assert!(config.multi_agent_v2.preserve_fork_prefix);
assert!(!config.multi_agent_v2.expose_spawn_agent_model_overrides);
assert!(!config.multi_agent_v2.wait_agent_enabled);
assert!(config.multi_agent_v2.disable_native_tools);
assert!(config.multi_agent_v2.disable_direct_message);
assert!(config.multi_agent_v2.message_board_in_memory);
assert!(config.multi_agent_v2.non_code_mode_only);
+6
View File
@@ -1381,6 +1381,7 @@ pub struct MultiAgentV2Config {
pub hide_spawn_agent_metadata: bool,
pub expose_spawn_agent_model_overrides: bool,
pub wait_agent_enabled: bool,
pub disable_native_tools: bool,
pub disable_direct_message: bool,
pub message_board_in_memory: bool,
pub message_board_remote: Option<codex_features::RemoteMessageBoardConfigToml>,
@@ -1404,6 +1405,7 @@ impl MultiAgentV2Config {
hide_spawn_agent_metadata: true,
expose_spawn_agent_model_overrides: true,
wait_agent_enabled: true,
disable_native_tools: false,
disable_direct_message: false,
message_board_in_memory: false,
message_board_remote: None,
@@ -2850,6 +2852,9 @@ fn resolve_multi_agent_v2_config(config_toml: &ConfigToml) -> MultiAgentV2Config
let wait_agent_enabled = base
.and_then(|config| config.wait_agent_enabled)
.unwrap_or(default.wait_agent_enabled);
let disable_native_tools = base
.and_then(|config| config.disable_native_tools)
.unwrap_or(default.disable_native_tools);
let disable_direct_message = base
.and_then(|config| config.disable_direct_message)
.unwrap_or(default.disable_direct_message);
@@ -2889,6 +2894,7 @@ fn resolve_multi_agent_v2_config(config_toml: &ConfigToml) -> MultiAgentV2Config
hide_spawn_agent_metadata,
expose_spawn_agent_model_overrides,
wait_agent_enabled,
disable_native_tools,
disable_direct_message,
message_board_in_memory,
message_board_remote: base.and_then(|config| config.message_board_remote.clone()),
+3 -2
View File
@@ -733,8 +733,9 @@ fn collab_tools_enabled(turn_context: &TurnContext, model_info: &ModelInfo) -> b
turn_context.config.agent_max_depth,
),
MultiAgentVersion::V2 => {
turn_context.session_source.get_agent_path().is_none()
|| model_info.multi_agent_version == Some(MultiAgentVersion::V2)
!turn_context.config.multi_agent_v2.disable_native_tools
&& (turn_context.session_source.get_agent_path().is_none()
|| model_info.multi_agent_version == Some(MultiAgentVersion::V2))
}
}
}
+23 -5
View File
@@ -1097,14 +1097,17 @@ async fn astra_omits_disabled_executor_and_plugin_skills_from_model_context(
Ok(())
}
#[test_case::test_case(false; "native_tools")]
#[test_case::test_case(true; "host_tools")]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn multi_agent_catalog_parameters() -> Result<()> {
async fn multi_agent_catalog_parameters(disable_native_tools: bool) -> Result<()> {
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let test = test_codex()
.with_config(|config| {
.with_config(move |config| {
configure_scenario_catalog(config);
config.multi_agent_v2.disable_native_tools = disable_native_tools;
config.workspace_roots = vec![config.cwd.clone()];
})
.with_model_info_override("gpt-6-astra", |model| {
@@ -1149,10 +1152,25 @@ async fn multi_agent_catalog_parameters() -> Result<()> {
.await;
test.submit_turn("Check which agents are working under /root before delegating more work.")
.await?;
insta::assert_snapshot!(
"multi_agent_catalog_parameters",
context_snapshot::format_request_history_snapshot(
assert_eq!(
test.codex.multi_agent_version(),
Some(codex_protocol::protocol::MultiAgentVersion::V2)
);
let (name, description) = if disable_native_tools {
(
"multi_agent_native_tools_hidden",
"Native tools are unavailable while the V2 runtime and its instructions remain enabled.",
)
} else {
(
"multi_agent_catalog_parameters",
"Astra calls list_agents using the selected catalog parameter schema.",
)
};
insta::assert_snapshot!(
name,
context_snapshot::format_request_history_snapshot(
description,
&mock.requests(),
&ContextSnapshotOptions::default().include_request_settings(),
)
@@ -0,0 +1,99 @@
---
source: core/tests/suite/scenarios.rs
assertion_line: 1272
expression: "context_snapshot::format_request_history_snapshot(description,\n&mock.requests(),\n&ContextSnapshotOptions::default().include_request_settings(),)"
---
Scenario: Native tools are unavailable while the V2 runtime and its instructions remain enabled.
## Window 1
Settings:
include: ["reasoning.encrypted_content"]
model: "gpt-6-astra"
parallel_tool_calls: false
prompt_cache_key: "<PROMPT_CACHE_KEY 1>"
reasoning: {"context":"all_turns","effort":"low"}
store: false
stream: true
text: {"verbosity":"low"}
tool_choice: "auto"
-- request 1 (turn) --
00:additional_tools/developer (2; hash=d56ea226c3637559):
- namespace/functions; hash=fada4a476a3754dd
- custom/exec
- function/wait
- function/request_user_input
- function/request_user_input_async
- namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317
- function/sleep
01:message/developer:
You are Codex, an agent based on GPT-6. You and the user share one workspace, and your job is to collabo...them until their intended goal is completely handled. [hash=efa50f074efa5803]
# When to ask the user for permission
Use your best judgement given task context for when you really need user permission, like a competent co...ork without ending the turn to clarify with the user. [hash=ca7367e658b51388]
User authorization and preferences persist across turns. Do not request permission again when the user h... any guidelines provided in skills or external files. [hash=911f91c92c72882c]
<OMITTED 155 LINES; ~4935 TOKENS; hash=5573869716192298>
- Skill naming: If a plugin contributes skills, those skill entries are prefixed with plugin_name: in the Skills list.
- MCP naming: Plugin-provided MCP tools keep standard MCP identifiers such as mcp__server__tool; use tool provenance to tell which plugin they come from.
- Trigger rules: If the user explicitly names a plugin, prefer capabilities associated with that plugin for that turn.
- Relationship to capabilities: Plugins are not invoked directly. Use their underlying skills, MCP tools, and app tools to help solve the task.
- Relevance: Determine what a plugin can help with from explicit user mention or from the plugin-associa..., MCP tools, and apps exposed elsewhere in this turn. [hash=620459a4e7b590f0]
- Missing/blocked: If the user requests a plugin that does not have relevant callable capabilities for t..., say so briefly and continue with the best fallback. [hash=746b0469a8f6cb3e]
02:message/developer[2]:
[01] <permissions instructions>
Filesystem sandboxing defines which files can be read or written. `sandbox_mode` is `danger-full-access`...ll commands are permitted. Network access is enabled. [hash=c134061cebb94a1b]
Approval policy is currently never. Do not provide the `sandbox_permissions` for any reason, commands will be rejected.
</permissions instructions>
[02] <collaboration_mode># Collaboration Mode: Default
You are now in Default mode. Any previous instructions for other modes (e.g. Plan mode) are no longer active.
Your active mode changes only when new developer instructions with a different `<collaboration_mode>...<...by themselves. Known mode names are Default and Plan. [hash=e75f6b12fc52d812]
## request_user_input availability
Use the `request_user_input` tool only when it is listed in the available tools for this turn.
Use the `request_user_input` tool only for optional questions where the answer would materially improve the quality of the work.
If `request_user_input` returns no answers, continue with best judgment instead of asking again or treating the turn as blocked.
Never use the `request_user_input` tool for permission requests or permission-related escalations.
</collaboration_mode>
03:message/developer:
<multi_agent_role>You are `/root`, the primary agent in a team of agents collaborating to fulfill the user's goals.
At the start of your turn, you are the active agent.
You can spawn sub-agents to handle subtasks, and those sub-agents can spawn their own sub-agents.
All agents in the team, including the agents that you can assign tasks to, are equally intelligent and capable, and have access to the same set of tools.
You can use `spawn_agent` to create a new agent, `followup_task` to give an existing agent a new task an...message to a running agent without triggering a turn. [hash=7d40ff8b58977202]
`send_message` calls may be read by a human, so ensure they are legible. Always put proper spaces between words and/or numbers.
<OMITTED 17 LINES; ~246 TOKENS; hash=780031e2df907944>
- All agents use the same current working directory.
- As a result, edits made by one agent are immediately visible to all other agents.
When calling `wait_agent`, prefer longer waits (minutes) to avoid busy polling.
There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you.
Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d... so, set `fork_turns` to `"none"`.</multi_agent_role> [hash=d5928243dc643bcc]
04:message/developer:
<multi_agent_mode>Any earlier instruction enabling proactive multi-agent delegation no longer applies. D...elegation, or parallel agent work.</multi_agent_mode> [hash=2b6e037067b4390c]
05:message/user:
<environment_context>
<cwd><CWD></cwd>
<shell><HOST_SHELL></shell>
<current_date><CURRENT_DATE></current_date>
<timezone><HOST_TIMEZONE></timezone>
<filesystem><workspace_roots><root><CWD></root></workspace_roots><permission_profile type="disabled"><...e="unrestricted" /></permission_profile></filesystem> [hash=d40b2f45cb028888]
</environment_context>
06:message/user:
Check which agents are working under /root before delegating more work.
-- request 2 (turn) --
07:function_call/collaboration.list_agents:{"path_prefix":"/root"}
08:function_call_output:unsupported call: collaborationlist_agents
+3
View File
@@ -331,6 +331,9 @@ pub struct MultiAgentV2ConfigToml {
/// Expose the multi-agent v2 `wait_agent` tool.
#[serde(skip_serializing_if = "Option::is_none")]
pub wait_agent_enabled: Option<bool>,
/// Hide native multi-agent tools while retaining the V2 runtime for host-provided tools.
#[serde(skip_serializing_if = "Option::is_none")]
pub disable_native_tools: Option<bool>,
/// Disable the model's direct-message tools; spawning and automatic child results remain available.
#[serde(skip_serializing_if = "Option::is_none")]
pub disable_direct_message: Option<bool>,
+2
View File
@@ -761,6 +761,7 @@ tool_namespace = "agents"
hide_spawn_agent_metadata = true
expose_spawn_agent_model_overrides = true
wait_agent_enabled = false
disable_native_tools = true
disable_direct_message = true
message_board_in_memory = true
non_code_mode_only = true
@@ -791,6 +792,7 @@ non_code_mode_only = true
hide_spawn_agent_metadata: Some(true),
expose_spawn_agent_model_overrides: Some(true),
wait_agent_enabled: Some(false),
disable_native_tools: Some(true),
disable_direct_message: Some(true),
message_board_in_memory: Some(true),
message_board_remote: None,