mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Add request_user_input auto-resolution window contract (#27256)
## Why `request_user_input` is moving beyond its original plan-mode-only workflow, and future default/goal-mode usage needs a way for the model to ask helpful but non-blocking questions without forcing the turn to wait forever. This PR adds an explicit `autoResolutionMs` contract so a later client/runtime change can auto-resolve unanswered prompts after a bounded window while leaving truly blocking questions unchanged. This is contract plumbing only; it does not implement the client-side timer or auto-selection behavior, and the model-facing description treats the field as reserved unless the current runtime explicitly supports auto-resolution. ## What Changed - Added optional `autoResolutionMs` to the model-facing `request_user_input` args and core `RequestUserInputEvent`. - Added model-facing schema text for `autoResolutionMs` while marking it reserved for runtimes that explicitly support auto-resolution. - Bounds `autoResolutionMs` to `60_000..=240_000` ms during argument normalization by clamping out-of-range model-provided values. - Propagated the field through app-server v2 `ToolRequestUserInputParams`, app-server request forwarding, generated TypeScript, and JSON schema fixtures. - Updated app-server, core, protocol, and TUI call sites/tests so omitted values preserve existing `None`/`null` behavior and coverage verifies a `Some(60_000)` round trip. ## Verification - `just test -p codex-app-server-protocol` - `just test -p codex-core request_user_input` - `just test -p codex-app-server request_user_input_round_trip` - `just test -p codex-tui request_user_input` - `just test -p codex-protocol`
This commit is contained in:
@@ -509,6 +509,7 @@ mod tests {
|
||||
turn_id: "turn-2".to_string(),
|
||||
item_id: "tool-1".to_string(),
|
||||
questions: Vec::new(),
|
||||
auto_resolution_ms: None,
|
||||
},
|
||||
}),
|
||||
None
|
||||
@@ -798,6 +799,7 @@ mod tests {
|
||||
turn_id: "turn-1".to_string(),
|
||||
item_id: "tool-1".to_string(),
|
||||
questions: Vec::new(),
|
||||
auto_resolution_ms: None,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -820,6 +822,7 @@ mod tests {
|
||||
turn_id: "turn-1".to_string(),
|
||||
item_id: item_id.to_string(),
|
||||
questions: Vec::new(),
|
||||
auto_resolution_ms: None,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -597,6 +597,7 @@ mod tests {
|
||||
turn_id: turn_id.to_string(),
|
||||
item_id: call_id.to_string(),
|
||||
questions: Vec::new(),
|
||||
auto_resolution_ms: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4586,6 +4586,7 @@ fn request_user_input_request(thread_id: ThreadId, turn_id: &str, item_id: &str)
|
||||
turn_id: turn_id.to_string(),
|
||||
item_id: item_id.to_string(),
|
||||
questions: Vec::new(),
|
||||
auto_resolution_ms: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1538,6 +1538,7 @@ mod tests {
|
||||
item_id: "call-1".to_string(),
|
||||
turn_id: turn_id.to_string(),
|
||||
questions,
|
||||
auto_resolution_ms: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1600,12 +1601,14 @@ mod tests {
|
||||
item_id: "call-2".to_string(),
|
||||
turn_id: "turn-2".to_string(),
|
||||
questions: vec![question_with_options("q2", "Second")],
|
||||
auto_resolution_ms: None,
|
||||
});
|
||||
overlay.try_consume_user_input_request(ToolRequestUserInputParams {
|
||||
thread_id: "thread-1".to_string(),
|
||||
item_id: "call-3".to_string(),
|
||||
turn_id: "turn-3".to_string(),
|
||||
questions: vec![question_with_options("q3", "Third")],
|
||||
auto_resolution_ms: None,
|
||||
});
|
||||
|
||||
overlay.handle_key_event(KeyEvent::from(KeyCode::Esc));
|
||||
@@ -1623,6 +1626,7 @@ mod tests {
|
||||
item_id: "call-1".to_string(),
|
||||
turn_id: "turn-1".to_string(),
|
||||
questions: vec![question_with_options("q1", "First")],
|
||||
auto_resolution_ms: None,
|
||||
},
|
||||
tx,
|
||||
/*has_input_focus*/ true,
|
||||
@@ -1651,6 +1655,7 @@ mod tests {
|
||||
item_id: "call-1".to_string(),
|
||||
turn_id: "turn-1".to_string(),
|
||||
questions: vec![question_with_options("q1", "First")],
|
||||
auto_resolution_ms: None,
|
||||
},
|
||||
tx,
|
||||
/*has_input_focus*/ true,
|
||||
@@ -1662,6 +1667,7 @@ mod tests {
|
||||
item_id: "call-2".to_string(),
|
||||
turn_id: "turn-1".to_string(),
|
||||
questions: vec![question_with_options("q2", "Second")],
|
||||
auto_resolution_ms: None,
|
||||
});
|
||||
|
||||
assert!(
|
||||
@@ -1689,6 +1695,7 @@ mod tests {
|
||||
item_id: "call-1".to_string(),
|
||||
turn_id: "turn-1".to_string(),
|
||||
questions: vec![question_with_options("q1", "First")],
|
||||
auto_resolution_ms: None,
|
||||
},
|
||||
tx,
|
||||
/*has_input_focus*/ true,
|
||||
@@ -1700,12 +1707,14 @@ mod tests {
|
||||
item_id: "call-2".to_string(),
|
||||
turn_id: "turn-1".to_string(),
|
||||
questions: vec![question_with_options("q2", "Second")],
|
||||
auto_resolution_ms: None,
|
||||
});
|
||||
overlay.try_consume_user_input_request(ToolRequestUserInputParams {
|
||||
thread_id: "thread-1".to_string(),
|
||||
item_id: "call-3".to_string(),
|
||||
turn_id: "turn-1".to_string(),
|
||||
questions: vec![question_with_options("q3", "Third")],
|
||||
auto_resolution_ms: None,
|
||||
});
|
||||
|
||||
assert!(
|
||||
|
||||
@@ -152,6 +152,7 @@ mod tests {
|
||||
item_id: call_id.to_string(),
|
||||
turn_id: turn_id.to_string(),
|
||||
questions: Vec::new(),
|
||||
auto_resolution_ms: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -597,6 +597,7 @@ async fn request_user_input_notification_overrides_pending_agent_turn_complete_n
|
||||
description: "Update only Plan mode.".to_string(),
|
||||
}]),
|
||||
}],
|
||||
auto_resolution_ms: None,
|
||||
});
|
||||
|
||||
assert_matches!(
|
||||
@@ -626,6 +627,7 @@ async fn handle_request_user_input_sets_pending_notification() {
|
||||
description: "Update only Plan mode.".to_string(),
|
||||
}]),
|
||||
}],
|
||||
auto_resolution_ms: None,
|
||||
});
|
||||
|
||||
assert_matches!(
|
||||
|
||||
Reference in New Issue
Block a user