Add structured asynchronous user input requests (#42178)

## What changed

- Replace `send_user_message_async` with `request_user_input_async`, accepting one or more questions with optional suggested answers while allowing the turn to continue.
- Attach structured question metadata to asynchronous agent messages while retaining readable fallback text, and preserve it through app-server events, thread history, and generated schemas.
- Continue enabling the new tool for model catalogs that advertise either the old or new tool name.

## Testing

- Cover tool registration, question validation, emitted items, continued turn execution, history preservation, and backwards-compatible deserialization.

GitOrigin-RevId: ffc49aebde2e854c3c50a122aa52805fec3fc6b3
This commit is contained in:
rka-oai authored and copyberry committed 2026-09-02 02:09:35 +00:00
1 parent b21f2e3081
commit 2c79ee6dac
80 files changed
+1110 -146

No files matched your search

@@ -2045,6 +2045,7 @@ async fn external_agent_config_import_creates_session_rollouts() -> Result<()> {
phase: None,
memory_citation: None,
delivery: None,
questions: None,
})
);
@@ -260,8 +260,8 @@ async fn history_notes_and_async_message_emit_control_tool_analytics() -> Result
),
(
"functions",
"send_user_message_async",
json!({"message": "PRIVATE_MESSAGE"}),
"request_user_input_async",
json!({"questions": [{"title": "PRIVATE_MESSAGE"}]}),
),
(
"notes",
@@ -270,8 +270,8 @@ async fn history_notes_and_async_message_emit_control_tool_analytics() -> Result
),
(
"functions",
"send_user_message_async",
json!({"message": " "}),
"request_user_input_async",
json!({"questions": [{"title": " "}]}),
),
];
let server = responses::start_mock_server().await;
@@ -584,6 +584,7 @@ async fn thread_search_occurrences_reads_paginated_projection() -> Result<()> {
phase: Some(MessagePhase::Commentary),
memory_citation: None,
delivery: None,
questions: None,
}),
),
paginated_completed_item(
@@ -597,6 +598,7 @@ async fn thread_search_occurrences_reads_paginated_projection() -> Result<()> {
phase: Some(MessagePhase::FinalAnswer),
memory_citation: None,
delivery: None,
questions: None,
}),
),
paginated_turn_completed("turn-1"),
@@ -1627,6 +1629,7 @@ async fn paginated_history_lists_and_legacy_reads_use_projected_turns_and_items(
phase: None,
memory_citation: None,
delivery: None,
questions: None,
}),
),
paginated_completed_item(
@@ -1679,6 +1682,7 @@ async fn paginated_history_lists_and_legacy_reads_use_projected_turns_and_items(
phase: None,
memory_citation: None,
delivery: None,
questions: None,
},
],
items_view: TurnItemsView::Full,
@@ -1865,6 +1869,7 @@ async fn paginated_history_lists_and_legacy_reads_use_projected_turns_and_items(
phase: None,
memory_citation: None,
delivery: None,
questions: None,
},
],
items_view: TurnItemsView::Summary,
@@ -2111,6 +2116,7 @@ fn append_agent_message(path: &Path, timestamp: &str, text: &str) -> anyhow::Res
phase: None,
memory_citation: None,
delivery: None,
questions: None,
}))?,
})
)?;
@@ -3836,6 +3836,7 @@ async fn thread_resume_token_usage_replay_ignores_stale_interrupted_tail_turn()
phase: None,
memory_citation: None,
delivery: None,
questions: None,
}))?,
})
.to_string(),
@@ -3923,6 +3924,7 @@ async fn thread_resume_token_usage_replay_can_belong_to_interrupted_turn() -> Re
phase: None,
memory_citation: None,
delivery: None,
questions: None,
}))?,
})
.to_string(),
@@ -4233,6 +4235,7 @@ async fn thread_resume_and_read_interrupt_incomplete_rollout_turn_when_thread_is
phase: None,
memory_citation: None,
delivery: None,
questions: None,
}))?,
})
.to_string(),