mirror of
https://github.com/openai/codex.git
synced 2026-10-23 18:08:45 +00:00
Make user input blocking behavior explicit (#36410)
## Why Clients need to know whether a `request_user_input` request must wait for an explicit response or may auto-resolve. Using `autoResolutionMs` as that signal conflated the blocking decision with timeout policy. ## What changed - Add required `isBlocking` fields to user input protocol and app-server request payloads. Plan-mode requests are blocking, while requests from other enabled modes are non-blocking. - Drive the TUI's auto-resolution behavior from `isBlocking` and remove `autoResolutionMs` from the model-facing tool schema. - Deprecate `autoResolutionMs` while retaining it for compatibility, and treat legacy payloads without `isBlocking` as blocking. ## Testing - Cover mode-derived blocking behavior, legacy deserialization, app-server forwarding, delegated requests, and TUI auto-resolution. GitOrigin-RevId: 29aade657ef743065ec264376ba567a9b353d7d7
This commit is contained in:
@@ -1624,7 +1624,7 @@ pub struct ToolRequestUserInputQuestion {
|
||||
pub options: Option<Vec<ToolRequestUserInputOption>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[derive(Serialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
/// EXPERIMENTAL. Params sent with a request_user_input event.
|
||||
@@ -1633,11 +1633,41 @@ pub struct ToolRequestUserInputParams {
|
||||
pub turn_id: String,
|
||||
pub item_id: String,
|
||||
pub questions: Vec<ToolRequestUserInputQuestion>,
|
||||
pub is_blocking: bool,
|
||||
/// @deprecated Use `isBlocking` to decide whether the request should block.
|
||||
#[serde(default)]
|
||||
#[ts(type = "number | null")]
|
||||
pub auto_resolution_ms: Option<u64>,
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ToolRequestUserInputParams {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct WireToolRequestUserInputParams {
|
||||
thread_id: String,
|
||||
turn_id: String,
|
||||
item_id: String,
|
||||
questions: Vec<ToolRequestUserInputQuestion>,
|
||||
is_blocking: Option<bool>,
|
||||
auto_resolution_ms: Option<u64>,
|
||||
}
|
||||
|
||||
let wire = WireToolRequestUserInputParams::deserialize(deserializer)?;
|
||||
Ok(Self {
|
||||
thread_id: wire.thread_id,
|
||||
turn_id: wire.turn_id,
|
||||
item_id: wire.item_id,
|
||||
questions: wire.questions,
|
||||
is_blocking: wire.is_blocking.unwrap_or(true),
|
||||
auto_resolution_ms: wire.auto_resolution_ms,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
|
||||
@@ -4731,6 +4731,7 @@ fn realtime_start_omitted_initial_items_remain_none() {
|
||||
|
||||
assert_eq!(params.initial_items, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn realtime_start_deserializes_client_handoff_channel_prefixes() {
|
||||
let params = serde_json::from_value::<ThreadRealtimeStartParams>(json!({
|
||||
@@ -4756,3 +4757,45 @@ fn realtime_start_deserializes_client_handoff_channel_prefixes() {
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tool_request_user_input_params_default_legacy_missing_is_blocking_to_true() {
|
||||
let params = serde_json::from_value::<ToolRequestUserInputParams>(json!({
|
||||
"threadId": "thread-1",
|
||||
"turnId": "turn-1",
|
||||
"itemId": "call-1",
|
||||
"questions": [{
|
||||
"id": "q1",
|
||||
"header": "Confirm",
|
||||
"question": "Continue?",
|
||||
"options": [{
|
||||
"label": "Yes",
|
||||
"description": "Continue."
|
||||
}]
|
||||
}],
|
||||
"autoResolutionMs": 60_000
|
||||
}))
|
||||
.expect("legacy request_user_input params should deserialize");
|
||||
|
||||
assert_eq!(
|
||||
params,
|
||||
ToolRequestUserInputParams {
|
||||
thread_id: "thread-1".to_string(),
|
||||
turn_id: "turn-1".to_string(),
|
||||
item_id: "call-1".to_string(),
|
||||
questions: vec![ToolRequestUserInputQuestion {
|
||||
id: "q1".to_string(),
|
||||
header: "Confirm".to_string(),
|
||||
question: "Continue?".to_string(),
|
||||
is_other: false,
|
||||
is_secret: false,
|
||||
options: Some(vec![ToolRequestUserInputOption {
|
||||
label: "Yes".to_string(),
|
||||
description: "Continue.".to_string(),
|
||||
}]),
|
||||
}],
|
||||
is_blocking: true,
|
||||
auto_resolution_ms: Some(60_000),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user