Chore: plan mode do not include free form question and always include isOther (#10210)

We should never ask a freeform question when planning and we should
always include isOther as an escape hatch.
This commit is contained in:
Shijie Rao
2026-01-30 01:19:24 -08:00
committed by GitHub
parent c0cad80668
commit a0ccef9d5c
6 changed files with 18 additions and 17 deletions

View File

@@ -2593,7 +2593,7 @@ pub struct ToolRequestUserInputOption {
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
/// EXPERIMENTAL. Represents one request_user_input question and its optional options.
/// EXPERIMENTAL. Represents one request_user_input question and its required options.
pub struct ToolRequestUserInputQuestion {
pub id: String,
pub header: String,

View File

@@ -67,7 +67,6 @@ pub fn create_request_user_input_sse_response(call_id: &str) -> anyhow::Result<S
"id": "confirm_path",
"header": "Confirm",
"question": "Proceed with the plan?",
"isOther": false,
"options": [{
"label": "Yes (Recommended)",
"description": "Continue the current plan."

View File

@@ -49,7 +49,19 @@ impl ToolHandler for RequestUserInputHandler {
)));
}
let args: RequestUserInputArgs = parse_arguments(&arguments)?;
let mut args: RequestUserInputArgs = parse_arguments(&arguments)?;
let missing_options = args
.questions
.iter()
.any(|question| question.options.as_ref().is_none_or(Vec::is_empty));
if missing_options {
return Err(FunctionCallError::RespondToModel(
"request_user_input requires non-empty options for every question".to_string(),
));
}
for question in &mut args.questions {
question.is_other = true;
}
let response = session
.request_user_input(turn.as_ref(), call_id, args)
.await

View File

@@ -571,7 +571,7 @@ fn create_request_user_input_tool() -> ToolSpec {
let options_schema = JsonSchema::Array {
description: Some(
"Optional 2-3 mutually exclusive choices. Put the recommended option first and suffix its label with \"(Recommended)\". Do not include an \"Other\" option in this list; use isOther on the question to request a free form choice. If the question is free form in nature, please do not have any option."
"Provide 2-3 mutually exclusive choices. Put the recommended option first and suffix its label with \"(Recommended)\". Do not include an \"Other\" option in this list; the client will add a free-form \"Other\" option automatically."
.to_string(),
),
items: Box::new(JsonSchema::Object {
@@ -602,15 +602,6 @@ fn create_request_user_input_tool() -> ToolSpec {
description: Some("Single-sentence prompt shown to the user.".to_string()),
},
);
question_props.insert(
"isOther".to_string(),
JsonSchema::Boolean {
description: Some(
"True when this question should include a free-form \"Other\" option. Otherwise false."
.to_string(),
),
},
);
question_props.insert("options".to_string(), options_schema);
let questions_schema = JsonSchema::Array {
@@ -621,7 +612,7 @@ fn create_request_user_input_tool() -> ToolSpec {
"id".to_string(),
"header".to_string(),
"question".to_string(),
"isOther".to_string(),
"options".to_string(),
]),
additional_properties: Some(false.into()),
}),

View File

@@ -94,7 +94,6 @@ async fn request_user_input_round_trip_resolves_pending() -> anyhow::Result<()>
"id": "confirm_path",
"header": "Confirm",
"question": "Proceed with the plan?",
"isOther": false,
"options": [{
"label": "Yes (Recommended)",
"description": "Continue the current plan."
@@ -153,6 +152,7 @@ async fn request_user_input_round_trip_resolves_pending() -> anyhow::Result<()>
.await;
assert_eq!(request.call_id, call_id);
assert_eq!(request.questions.len(), 1);
assert_eq!(request.questions[0].is_other, true);
let mut answers = HashMap::new();
answers.insert(
@@ -214,7 +214,6 @@ where
"id": "confirm_path",
"header": "Confirm",
"question": "Proceed with the plan?",
"isOther": false,
"options": [{
"label": "Yes (Recommended)",
"description": "Continue the current plan."

View File

@@ -75,7 +75,7 @@ For complete documentation of the `Op` and `EventMsg` variants, refer to [protoc
- `EventMsg`
- `EventMsg::AgentMessage` Messages from the `Model`
- `EventMsg::ExecApprovalRequest` Request approval from user to execute a command
- `EventMsg::RequestUserInput` Request user input for a tool call (questions can include options plus `isOther` to add a free-form choice)
- `EventMsg::RequestUserInput` Request user input for a tool call (questions must include options; the client always adds a free-form choice)
- `EventMsg::TurnComplete` A turn completed successfully
- `EventMsg::Error` A turn stopped with an error
- `EventMsg::Warning` A non-fatal warning that the client should surface to the user