Rename multi-agent wait tool to wait_agent (#14631)

- rename the multi-agent tool name the model sees to wait_agent
- update the model-facing prompts and tool descriptions to match

---------

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Ahmed Ibrahim
2026-03-13 14:38:05 -07:00
committed by GitHub
Unverified
parent 6720caf778
commit cfd97b36da
7 changed files with 58 additions and 54 deletions
@@ -52,7 +52,7 @@ pub(crate) use close_agent::Handler as CloseAgentHandler;
pub(crate) use resume_agent::Handler as ResumeAgentHandler;
pub(crate) use send_input::Handler as SendInputHandler;
pub(crate) use spawn::Handler as SpawnAgentHandler;
pub(crate) use wait::Handler as WaitHandler;
pub(crate) use wait::Handler as WaitAgentHandler;
/// Minimum wait timeout to prevent tight polling loops from burning CPU.
pub(crate) const MIN_WAIT_TIMEOUT_MS: i64 = 10_000;
@@ -14,7 +14,7 @@ pub(crate) struct Handler;
#[async_trait]
impl ToolHandler for Handler {
type Output = WaitResult;
type Output = WaitAgentResult;
fn kind(&self) -> ToolKind {
ToolKind::Function
@@ -153,7 +153,7 @@ impl ToolHandler for Handler {
let statuses_map = statuses.clone().into_iter().collect::<HashMap<_, _>>();
let agent_statuses = build_wait_agent_statuses(&statuses_map, &receiver_agents);
let result = WaitResult {
let result = WaitAgentResult {
status: statuses_map.clone(),
timed_out: statuses.is_empty(),
};
@@ -182,14 +182,14 @@ struct WaitArgs {
}
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
pub(crate) struct WaitResult {
pub(crate) struct WaitAgentResult {
pub(crate) status: HashMap<ThreadId, AgentStatus>,
pub(crate) timed_out: bool,
}
impl ToolOutput for WaitResult {
impl ToolOutput for WaitAgentResult {
fn log_preview(&self) -> String {
tool_output_json_text(self, "wait")
tool_output_json_text(self, "wait_agent")
}
fn success_for_logging(&self) -> bool {
@@ -197,11 +197,11 @@ impl ToolOutput for WaitResult {
}
fn to_response_item(&self, call_id: &str, payload: &ToolPayload) -> ResponseInputItem {
tool_output_response_item(call_id, payload, self, None, "wait")
tool_output_response_item(call_id, payload, self, None, "wait_agent")
}
fn code_mode_result(&self, _payload: &ToolPayload) -> JsonValue {
tool_output_code_mode_result(self, "wait")
tool_output_code_mode_result(self, "wait_agent")
}
}
@@ -737,18 +737,18 @@ async fn resume_agent_rejects_when_depth_limit_exceeded() {
}
#[tokio::test]
async fn wait_rejects_non_positive_timeout() {
async fn wait_agent_rejects_non_positive_timeout() {
let (session, turn) = make_session_and_context().await;
let invocation = invocation(
Arc::new(session),
Arc::new(turn),
"wait",
"wait_agent",
function_payload(json!({
"ids": [ThreadId::new().to_string()],
"timeout_ms": 0
})),
);
let Err(err) = WaitHandler.handle(invocation).await else {
let Err(err) = WaitAgentHandler.handle(invocation).await else {
panic!("non-positive timeout should be rejected");
};
assert_eq!(
@@ -758,15 +758,15 @@ async fn wait_rejects_non_positive_timeout() {
}
#[tokio::test]
async fn wait_rejects_invalid_id() {
async fn wait_agent_rejects_invalid_id() {
let (session, turn) = make_session_and_context().await;
let invocation = invocation(
Arc::new(session),
Arc::new(turn),
"wait",
"wait_agent",
function_payload(json!({"ids": ["invalid"]})),
);
let Err(err) = WaitHandler.handle(invocation).await else {
let Err(err) = WaitAgentHandler.handle(invocation).await else {
panic!("invalid id should be rejected");
};
let FunctionCallError::RespondToModel(msg) = err else {
@@ -776,15 +776,15 @@ async fn wait_rejects_invalid_id() {
}
#[tokio::test]
async fn wait_rejects_empty_ids() {
async fn wait_agent_rejects_empty_ids() {
let (session, turn) = make_session_and_context().await;
let invocation = invocation(
Arc::new(session),
Arc::new(turn),
"wait",
"wait_agent",
function_payload(json!({"ids": []})),
);
let Err(err) = WaitHandler.handle(invocation).await else {
let Err(err) = WaitAgentHandler.handle(invocation).await else {
panic!("empty ids should be rejected");
};
assert_eq!(
@@ -794,7 +794,7 @@ async fn wait_rejects_empty_ids() {
}
#[tokio::test]
async fn wait_returns_not_found_for_missing_agents() {
async fn wait_agent_returns_not_found_for_missing_agents() {
let (mut session, turn) = make_session_and_context().await;
let manager = thread_manager();
session.services.agent_control = manager.agent_control();
@@ -803,22 +803,22 @@ async fn wait_returns_not_found_for_missing_agents() {
let invocation = invocation(
Arc::new(session),
Arc::new(turn),
"wait",
"wait_agent",
function_payload(json!({
"ids": [id_a.to_string(), id_b.to_string()],
"timeout_ms": 1000
})),
);
let output = WaitHandler
let output = WaitAgentHandler
.handle(invocation)
.await
.expect("wait should succeed");
.expect("wait_agent should succeed");
let (content, success) = expect_text_output(output);
let result: wait::WaitResult =
serde_json::from_str(&content).expect("wait result should be json");
let result: wait::WaitAgentResult =
serde_json::from_str(&content).expect("wait_agent result should be json");
assert_eq!(
result,
wait::WaitResult {
wait::WaitAgentResult {
status: HashMap::from([(id_a, AgentStatus::NotFound), (id_b, AgentStatus::NotFound),]),
timed_out: false
}
@@ -827,7 +827,7 @@ async fn wait_returns_not_found_for_missing_agents() {
}
#[tokio::test]
async fn wait_times_out_when_status_is_not_final() {
async fn wait_agent_times_out_when_status_is_not_final() {
let (mut session, turn) = make_session_and_context().await;
let manager = thread_manager();
session.services.agent_control = manager.agent_control();
@@ -837,22 +837,22 @@ async fn wait_times_out_when_status_is_not_final() {
let invocation = invocation(
Arc::new(session),
Arc::new(turn),
"wait",
"wait_agent",
function_payload(json!({
"ids": [agent_id.to_string()],
"timeout_ms": MIN_WAIT_TIMEOUT_MS
})),
);
let output = WaitHandler
let output = WaitAgentHandler
.handle(invocation)
.await
.expect("wait should succeed");
.expect("wait_agent should succeed");
let (content, success) = expect_text_output(output);
let result: wait::WaitResult =
serde_json::from_str(&content).expect("wait result should be json");
let result: wait::WaitAgentResult =
serde_json::from_str(&content).expect("wait_agent result should be json");
assert_eq!(
result,
wait::WaitResult {
wait::WaitAgentResult {
status: HashMap::new(),
timed_out: true
}
@@ -867,7 +867,7 @@ async fn wait_times_out_when_status_is_not_final() {
}
#[tokio::test]
async fn wait_clamps_short_timeouts_to_minimum() {
async fn wait_agent_clamps_short_timeouts_to_minimum() {
let (mut session, turn) = make_session_and_context().await;
let manager = thread_manager();
session.services.agent_control = manager.agent_control();
@@ -877,17 +877,21 @@ async fn wait_clamps_short_timeouts_to_minimum() {
let invocation = invocation(
Arc::new(session),
Arc::new(turn),
"wait",
"wait_agent",
function_payload(json!({
"ids": [agent_id.to_string()],
"timeout_ms": 10
})),
);
let early = timeout(Duration::from_millis(50), WaitHandler.handle(invocation)).await;
let early = timeout(
Duration::from_millis(50),
WaitAgentHandler.handle(invocation),
)
.await;
assert!(
early.is_err(),
"wait should not return before the minimum timeout clamp"
"wait_agent should not return before the minimum timeout clamp"
);
let _ = thread
@@ -898,7 +902,7 @@ async fn wait_clamps_short_timeouts_to_minimum() {
}
#[tokio::test]
async fn wait_returns_final_status_without_timeout() {
async fn wait_agent_returns_final_status_without_timeout() {
let (mut session, turn) = make_session_and_context().await;
let manager = thread_manager();
session.services.agent_control = manager.agent_control();
@@ -923,22 +927,22 @@ async fn wait_returns_final_status_without_timeout() {
let invocation = invocation(
Arc::new(session),
Arc::new(turn),
"wait",
"wait_agent",
function_payload(json!({
"ids": [agent_id.to_string()],
"timeout_ms": 1000
})),
);
let output = WaitHandler
let output = WaitAgentHandler
.handle(invocation)
.await
.expect("wait should succeed");
.expect("wait_agent should succeed");
let (content, success) = expect_text_output(output);
let result: wait::WaitResult =
serde_json::from_str(&content).expect("wait result should be json");
let result: wait::WaitAgentResult =
serde_json::from_str(&content).expect("wait_agent result should be json");
assert_eq!(
result,
wait::WaitResult {
wait::WaitAgentResult {
status: HashMap::from([(agent_id, AgentStatus::Shutdown)]),
timed_out: false
}
+7 -7
View File
@@ -1053,7 +1053,7 @@ fn create_spawn_agent_tool(config: &ToolsConfig) -> ToolSpec {
- For code-edit subtasks, decompose work so each delegated task has a disjoint write set.
### After you delegate
- Call wait very sparingly. Only call wait when you need the result immediately for the next critical-path step and you are blocked until it returns.
- Call wait_agent very sparingly. Only call wait_agent when you need the result immediately for the next critical-path step and you are blocked until it returns.
- Do not redo delegated subagent tasks yourself; focus on integrating results or tackling non-overlapping work.
- While the subagent is running in the background, do meaningful non-overlapping work immediately.
- Do not repeatedly wait by reflex.
@@ -1290,7 +1290,7 @@ fn create_resume_agent_tool() -> ToolSpec {
ToolSpec::Function(ResponsesApiTool {
name: "resume_agent".to_string(),
description:
"Resume a previously closed agent by id so it can receive send_input and wait calls."
"Resume a previously closed agent by id so it can receive send_input and wait_agent calls."
.to_string(),
strict: false,
defer_loading: None,
@@ -1303,7 +1303,7 @@ fn create_resume_agent_tool() -> ToolSpec {
})
}
fn create_wait_tool() -> ToolSpec {
fn create_wait_agent_tool() -> ToolSpec {
let mut properties = BTreeMap::new();
properties.insert(
"ids".to_string(),
@@ -1325,7 +1325,7 @@ fn create_wait_tool() -> ToolSpec {
);
ToolSpec::Function(ResponsesApiTool {
name: "wait".to_string(),
name: "wait_agent".to_string(),
description: "Wait for agents to reach a final status. Completed statuses may include the agent's final message. Returns empty status when timed out. Once the agent reaches a final status, a notification message will be received containing the same completed status."
.to_string(),
strict: false,
@@ -2441,7 +2441,7 @@ pub(crate) fn build_specs_with_discoverable_tools(
use crate::tools::handlers::multi_agents::ResumeAgentHandler;
use crate::tools::handlers::multi_agents::SendInputHandler;
use crate::tools::handlers::multi_agents::SpawnAgentHandler;
use crate::tools::handlers::multi_agents::WaitHandler;
use crate::tools::handlers::multi_agents::WaitAgentHandler;
use std::sync::Arc;
let mut builder = ToolRegistryBuilder::new();
@@ -2835,7 +2835,7 @@ pub(crate) fn build_specs_with_discoverable_tools(
);
push_tool_spec(
&mut builder,
create_wait_tool(),
create_wait_agent_tool(),
false,
config.code_mode_enabled,
);
@@ -2848,7 +2848,7 @@ pub(crate) fn build_specs_with_discoverable_tools(
builder.register_handler("spawn_agent", Arc::new(SpawnAgentHandler));
builder.register_handler("send_input", Arc::new(SendInputHandler));
builder.register_handler("resume_agent", Arc::new(ResumeAgentHandler));
builder.register_handler("wait", Arc::new(WaitHandler));
builder.register_handler("wait_agent", Arc::new(WaitAgentHandler));
builder.register_handler("close_agent", Arc::new(CloseAgentHandler));
}
+3 -3
View File
@@ -502,7 +502,7 @@ fn test_build_specs_collab_tools_enabled() {
let (tools, _) = build_specs(&tools_config, None, None, &[]).build();
assert_contains_tool_names(
&tools,
&["spawn_agent", "send_input", "wait", "close_agent"],
&["spawn_agent", "send_input", "wait_agent", "close_agent"],
);
assert_lacks_tool_name(&tools, "spawn_agents_on_csv");
}
@@ -530,7 +530,7 @@ fn test_build_specs_enable_fanout_enables_agent_jobs_and_collab_tools() {
&[
"spawn_agent",
"send_input",
"wait",
"wait_agent",
"close_agent",
"spawn_agents_on_csv",
],
@@ -651,7 +651,7 @@ fn test_build_specs_agent_job_worker_tools_enabled() {
"spawn_agent",
"send_input",
"resume_agent",
"wait",
"wait_agent",
"close_agent",
"spawn_agents_on_csv",
"report_agent_job_result",