Skip to content

Commit 4cc6818

Browse files
authored
chore: keep request_user_input tool to persist cache on multi-agents (openai#17009)
1 parent 413c1e1 commit 4cc6818

6 files changed

Lines changed: 93 additions & 21 deletions

File tree

codex-rs/core/src/tools/handlers/request_user_input.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::tools::context::ToolPayload;
55
use crate::tools::handlers::parse_arguments;
66
use crate::tools::registry::ToolHandler;
77
use crate::tools::registry::ToolKind;
8+
use codex_protocol::protocol::SessionSource;
89
use codex_protocol::request_user_input::RequestUserInputArgs;
910
use codex_tools::REQUEST_USER_INPUT_TOOL_NAME;
1011
use codex_tools::normalize_request_user_input_args;
@@ -39,6 +40,12 @@ impl ToolHandler for RequestUserInputHandler {
3940
}
4041
};
4142

43+
if matches!(turn.session_source, SessionSource::SubAgent(_)) {
44+
return Err(FunctionCallError::RespondToModel(
45+
"request_user_input can only be used by the root thread".to_string(),
46+
));
47+
}
48+
4249
let mode = session.collaboration_mode().await.mode;
4350
if let Some(message) =
4451
request_user_input_unavailable_message(mode, self.default_mode_request_user_input)
@@ -67,3 +74,7 @@ impl ToolHandler for RequestUserInputHandler {
6774
Ok(FunctionToolOutput::from_text(content, Some(true)))
6875
}
6976
}
77+
78+
#[cfg(test)]
79+
#[path = "request_user_input_tests.rs"]
80+
mod tests;
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use super::*;
2+
use crate::codex::make_session_and_context;
3+
use crate::tools::context::ToolInvocation;
4+
use crate::tools::context::ToolPayload;
5+
use crate::turn_diff_tracker::TurnDiffTracker;
6+
use codex_protocol::ThreadId;
7+
use codex_protocol::protocol::SubAgentSource;
8+
use pretty_assertions::assert_eq;
9+
use serde_json::json;
10+
use std::sync::Arc;
11+
use tokio::sync::Mutex;
12+
13+
#[tokio::test]
14+
async fn multi_agent_v2_request_user_input_rejects_subagent_threads() {
15+
let (session, mut turn) = make_session_and_context().await;
16+
turn.session_source = SessionSource::SubAgent(SubAgentSource::ThreadSpawn {
17+
parent_thread_id: ThreadId::new(),
18+
depth: 1,
19+
agent_path: None,
20+
agent_nickname: None,
21+
agent_role: None,
22+
});
23+
24+
let result = RequestUserInputHandler {
25+
default_mode_request_user_input: true,
26+
}
27+
.handle(ToolInvocation {
28+
session: Arc::new(session),
29+
turn: Arc::new(turn),
30+
tracker: Arc::new(Mutex::new(TurnDiffTracker::default())),
31+
call_id: "call-1".to_string(),
32+
tool_name: REQUEST_USER_INPUT_TOOL_NAME.to_string(),
33+
tool_namespace: None,
34+
payload: ToolPayload::Function {
35+
arguments: json!({
36+
"questions": [{
37+
"header": "Hdr",
38+
"question": "Pick one",
39+
"id": "pick_one",
40+
"options": [
41+
{
42+
"label": "A",
43+
"description": "A"
44+
},
45+
{
46+
"label": "B",
47+
"description": "B"
48+
}
49+
]
50+
}]
51+
})
52+
.to_string(),
53+
},
54+
})
55+
.await;
56+
57+
let Err(err) = result else {
58+
panic!("sub-agent request_user_input should fail");
59+
};
60+
assert_eq!(
61+
err,
62+
FunctionCallError::RespondToModel(
63+
"request_user_input can only be used by the root thread".to_string(),
64+
)
65+
);
66+
}

codex-rs/tools/src/tool_config.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ pub struct ToolsConfig {
105105
pub collab_tools: bool,
106106
pub multi_agent_v2: bool,
107107
pub hide_spawn_agent_metadata: bool,
108-
pub request_user_input: bool,
109108
pub default_mode_request_user_input: bool,
110109
pub experimental_supported_tools: Vec<String>,
111110
pub agent_jobs_tools: bool,
@@ -144,9 +143,8 @@ impl ToolsConfig {
144143
let include_multi_agent_v2 = features.enabled(Feature::MultiAgentV2);
145144
let hide_spawn_agent_metadata = features.enabled(Feature::DebugHideSpawnAgentMetadata);
146145
let include_agent_jobs = features.enabled(Feature::SpawnCsv);
147-
let include_request_user_input = !matches!(session_source, SessionSource::SubAgent(_));
148146
let include_default_mode_request_user_input =
149-
include_request_user_input && features.enabled(Feature::DefaultModeRequestUserInput);
147+
features.enabled(Feature::DefaultModeRequestUserInput);
150148
let include_search_tool =
151149
model_info.supports_search_tool && features.enabled(Feature::ToolSearch);
152150
let include_tool_suggest = features.enabled(Feature::ToolSuggest)
@@ -222,7 +220,6 @@ impl ToolsConfig {
222220
collab_tools: include_collab_tools,
223221
multi_agent_v2: include_multi_agent_v2,
224222
hide_spawn_agent_metadata,
225-
request_user_input: include_request_user_input,
226223
default_mode_request_user_input: include_default_mode_request_user_input,
227224
experimental_supported_tools: model_info.experimental_supported_tools.clone(),
228225
agent_jobs_tools: include_agent_jobs,

codex-rs/tools/src/tool_config_tests.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,10 @@ fn shell_zsh_fork_prefers_shell_command_over_unified_exec() {
131131
}
132132

133133
#[test]
134-
fn subagents_disable_request_user_input_and_agent_jobs_workers_opt_in_by_label() {
134+
fn subagents_keep_request_user_input_mode_config_and_agent_jobs_workers_opt_in_by_label() {
135135
let model_info = model_info();
136136
let mut features = Features::with_defaults();
137+
features.enable(Feature::DefaultModeRequestUserInput);
137138
features.enable(Feature::SpawnCsv);
138139

139140
let available_models = Vec::new();
@@ -149,8 +150,7 @@ fn subagents_disable_request_user_input_and_agent_jobs_workers_opt_in_by_label()
149150
windows_sandbox_level: WindowsSandboxLevel::Disabled,
150151
});
151152

152-
assert!(!tools_config.request_user_input);
153-
assert!(!tools_config.default_mode_request_user_input);
153+
assert!(tools_config.default_mode_request_user_input);
154154
assert!(tools_config.agent_jobs_tools);
155155
assert!(tools_config.agent_jobs_worker_tools);
156156
}

codex-rs/tools/src/tool_registry_plan.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -206,19 +206,17 @@ pub fn build_tool_registry_plan(
206206
plan.register_handler("js_repl_reset", ToolHandlerKind::JsReplReset);
207207
}
208208

209-
if config.request_user_input {
210-
plan.push_spec(
211-
create_request_user_input_tool(request_user_input_tool_description(
212-
config.default_mode_request_user_input,
213-
)),
214-
/*supports_parallel_tool_calls*/ false,
215-
config.code_mode_enabled,
216-
);
217-
plan.register_handler(
218-
REQUEST_USER_INPUT_TOOL_NAME,
219-
ToolHandlerKind::RequestUserInput,
220-
);
221-
}
209+
plan.push_spec(
210+
create_request_user_input_tool(request_user_input_tool_description(
211+
config.default_mode_request_user_input,
212+
)),
213+
/*supports_parallel_tool_calls*/ false,
214+
config.code_mode_enabled,
215+
);
216+
plan.register_handler(
217+
REQUEST_USER_INPUT_TOOL_NAME,
218+
ToolHandlerKind::RequestUserInput,
219+
);
222220

223221
if config.request_permissions_tool_enabled {
224222
plan.push_spec(

codex-rs/tools/src/tool_registry_plan_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,9 +534,9 @@ fn test_build_specs_agent_job_worker_tools_enabled() {
534534
"close_agent",
535535
"spawn_agents_on_csv",
536536
"report_agent_job_result",
537+
REQUEST_USER_INPUT_TOOL_NAME,
537538
],
538539
);
539-
assert_lacks_tool_name(&tools, "request_user_input");
540540
}
541541

542542
#[test]

0 commit comments

Comments
 (0)