Mirror user text into realtime (#17520)

- Let typed user messages submit while realtime is active and mirror
accepted text into the realtime text stream.
- Add integration coverage and snapshot for outbound realtime text.
This commit is contained in:
Ahmed Ibrahim
2026-04-12 15:03:14 -07:00
committed by GitHub
Unverified
parent cb870a169a
commit d840b247d7
7 changed files with 200 additions and 102 deletions
+43 -3
View File
@@ -2265,7 +2265,7 @@ impl Session {
}
pub(crate) async fn route_realtime_text_input(self: &Arc<Self>, text: String) {
handlers::user_input_or_turn(
handlers::user_input_or_turn_inner(
self,
self.next_internal_sub_id(),
Op::UserInput {
@@ -2276,6 +2276,7 @@ impl Session {
final_output_json_schema: None,
responsesapi_client_metadata: None,
},
/*mirror_user_text_to_realtime*/ None,
)
.await;
}
@@ -4910,6 +4911,7 @@ mod handlers {
use codex_protocol::config_types::ModeKind;
use codex_protocol::config_types::Settings;
use codex_protocol::dynamic_tools::DynamicToolResponse;
use codex_protocol::items::UserMessageItem;
use codex_protocol::mcp::RequestId as ProtocolRequestId;
use codex_protocol::user_input::UserInput;
use codex_rmcp_client::ElicitationAction;
@@ -4917,6 +4919,7 @@ mod handlers {
use serde_json::Value;
use std::path::PathBuf;
use std::sync::Arc;
use tracing::debug;
use tracing::info;
use tracing::warn;
@@ -4958,6 +4961,21 @@ mod handlers {
}
pub async fn user_input_or_turn(sess: &Arc<Session>, sub_id: String, op: Op) {
user_input_or_turn_inner(
sess,
sub_id,
op,
/*mirror_user_text_to_realtime*/ Some(()),
)
.await;
}
pub(super) async fn user_input_or_turn_inner(
sess: &Arc<Session>,
sub_id: String,
op: Op,
mirror_user_text_to_realtime: Option<()>,
) {
let (items, updates, responsesapi_client_metadata) = match op {
Op::UserTurn {
cwd,
@@ -5023,7 +5041,7 @@ mod handlers {
};
sess.maybe_emit_unknown_model_warning_for_turn(current_context.as_ref())
.await;
match sess
let accepted_items = match sess
.steer_input(
items.clone(),
/*expected_turn_id*/ None,
@@ -5031,7 +5049,10 @@ mod handlers {
)
.await
{
Ok(_) => current_context.session_telemetry.user_prompt(&items),
Ok(_) => {
current_context.session_telemetry.user_prompt(&items);
Some(items)
}
Err(SteerInputError::NoActiveTurn(items)) => {
if let Some(responsesapi_client_metadata) = responsesapi_client_metadata {
current_context
@@ -5041,12 +5062,14 @@ mod handlers {
current_context.session_telemetry.user_prompt(&items);
sess.refresh_mcp_servers_if_requested(&current_context)
.await;
let accepted_items = items.clone();
sess.spawn_task(
Arc::clone(&current_context),
items,
crate::tasks::RegularTask::new(),
)
.await;
Some(accepted_items)
}
Err(err) => {
sess.send_event_raw(Event {
@@ -5054,7 +5077,24 @@ mod handlers {
msg: EventMsg::Error(err.to_error_event()),
})
.await;
None
}
};
if let (Some(items), Some(())) = (accepted_items, mirror_user_text_to_realtime) {
self::mirror_user_text_to_realtime(sess, &items).await;
}
}
async fn mirror_user_text_to_realtime(sess: &Arc<Session>, items: &[UserInput]) {
let text = UserMessageItem::new(items).message();
if text.is_empty() {
return;
}
if sess.conversation.running_state().await.is_none() {
return;
}
if let Err(err) = sess.conversation.text_in(text).await {
debug!("failed to mirror user text to realtime conversation: {err}");
}
}