mirror of
https://github.com/openai/codex.git
synced 2026-10-20 00:18:49 +00:00
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:
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
)
|
||||
|
||||
+99
@@ -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
|
||||
@@ -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>,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user