diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index ea12c23ba4..02ea36ffd2 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -215,6 +215,12 @@ use crate::hook_runtime::record_pending_input; use crate::hook_runtime::run_pending_session_start_hooks; use crate::hook_runtime::run_user_prompt_submit_hooks; use crate::guardian::routes_approval_to_guardian; +use crate::hook_runtime::PendingInputHookDisposition; +use crate::hook_runtime::inspect_pending_input; +use crate::hook_runtime::record_additional_contexts; +use crate::hook_runtime::record_pending_input; +use crate::hook_runtime::run_pending_session_start_hooks; +use crate::hook_runtime::run_user_prompt_submit_hooks; use crate::instructions::UserInstructions; use crate::mcp::CODEX_APPS_MCP_SERVER_NAME; use crate::mcp::McpManager; @@ -4237,6 +4243,21 @@ impl Session { } } + pub async fn prepend_pending_input_with_metadata( + &self, + input: Vec, + ) -> Result<(), ()> { + let mut active = self.active_turn.lock().await; + match active.as_mut() { + Some(at) => { + let mut ts = at.turn_state.lock().await; + ts.prepend_pending_input_with_metadata(input); + Ok(()) + } + None => Err(()), + } + } + pub async fn get_pending_input_with_metadata( &self, ) -> Vec<(ResponseInputItem, Option)> { @@ -6002,11 +6023,13 @@ pub(crate) async fn run_turn( let mut requeued_pending_input = false; let mut accepted_pending_input = Vec::new(); if !pending_response_items.is_empty() { - let mut pending_input_iter = pending_response_items.into_iter(); - while let Some((pending_input_item, message_metadata)) = pending_input_iter.next() { + let mut pending_input_iter = pending_response_items + .into_iter() + .map(|(input, metadata)| crate::state::PendingInputItem { input, metadata }); + while let Some(pending_input_item) = pending_input_iter.next() { match inspect_pending_input(&sess, &turn_context, pending_input_item).await { PendingInputHookDisposition::Accepted(pending_input) => { - accepted_pending_input.push((*pending_input, message_metadata)); + accepted_pending_input.push(*pending_input); } PendingInputHookDisposition::Blocked { additional_contexts, @@ -6027,8 +6050,8 @@ pub(crate) async fn run_turn( } let has_accepted_pending_input = !accepted_pending_input.is_empty(); - for (pending_input, message_metadata) in accepted_pending_input { - record_pending_input(&sess, &turn_context, pending_input, message_metadata).await; + for pending_input in accepted_pending_input { + record_pending_input(&sess, &turn_context, pending_input).await; } record_additional_contexts(&sess, &turn_context, blocked_pending_input_contexts).await; diff --git a/codex-rs/core/src/hook_runtime.rs b/codex-rs/core/src/hook_runtime.rs index d185e1a86d..686db72c71 100644 --- a/codex-rs/core/src/hook_runtime.rs +++ b/codex-rs/core/src/hook_runtime.rs @@ -6,7 +6,6 @@ use codex_hooks::UserPromptSubmitOutcome; use codex_hooks::UserPromptSubmitRequest; use codex_protocol::items::TurnItem; use codex_protocol::models::DeveloperInstructions; -use codex_protocol::models::ResponseInputItem; use codex_protocol::models::ResponseItem; use codex_protocol::models::ResponseItemMetadata; use codex_protocol::protocol::AskForApproval; @@ -18,6 +17,7 @@ use codex_protocol::user_input::UserInput; use crate::codex::Session; use crate::codex::TurnContext; use crate::event_mapping::parse_turn_item; +use crate::state::PendingInputItem; pub(crate) struct HookRuntimeOutcome { pub should_stop: bool, @@ -33,6 +33,7 @@ pub(crate) enum PendingInputRecord { UserMessage { content: Vec, response_item: ResponseItem, + message_metadata: Option, additional_contexts: Vec, }, ConversationItem { @@ -137,8 +138,12 @@ pub(crate) async fn run_user_prompt_submit_hooks( pub(crate) async fn inspect_pending_input( sess: &Arc, turn_context: &Arc, - pending_input_item: ResponseInputItem, + pending_input_item: PendingInputItem, ) -> PendingInputHookDisposition { + let PendingInputItem { + input: pending_input_item, + metadata: message_metadata, + } = pending_input_item; let response_item = ResponseItem::from(pending_input_item); if let Some(TurnItem::UserMessage(user_message)) = parse_turn_item(&response_item) { let user_prompt_submit_outcome = @@ -151,6 +156,7 @@ pub(crate) async fn inspect_pending_input( PendingInputHookDisposition::Accepted(Box::new(PendingInputRecord::UserMessage { content: user_message.content, response_item, + message_metadata, additional_contexts: user_prompt_submit_outcome.additional_contexts, })) } @@ -165,12 +171,12 @@ pub(crate) async fn record_pending_input( sess: &Arc, turn_context: &Arc, pending_input: PendingInputRecord, - message_metadata: Option, ) { match pending_input { PendingInputRecord::UserMessage { content, response_item, + message_metadata, additional_contexts, } => { sess.record_user_prompt_and_emit_turn_item( diff --git a/codex-rs/core/src/state/turn.rs b/codex-rs/core/src/state/turn.rs index bdb79c4ba3..e4b4c1c9ed 100644 --- a/codex-rs/core/src/state/turn.rs +++ b/codex-rs/core/src/state/turn.rs @@ -284,6 +284,15 @@ impl TurnState { .push(PendingInputItem { input, metadata }); } + pub(crate) fn prepend_pending_input_with_metadata(&mut self, mut input: Vec) { + if input.is_empty() { + return; + } + + input.append(&mut self.pending_input); + self.pending_input = input; + } + pub(crate) fn take_pending_input_with_metadata(&mut self) -> Vec { if self.pending_input.is_empty() { Vec::with_capacity(0) diff --git a/codex-rs/core/src/tasks/mod.rs b/codex-rs/core/src/tasks/mod.rs index ef03f0045c..af8fbedbdf 100644 --- a/codex-rs/core/src/tasks/mod.rs +++ b/codex-rs/core/src/tasks/mod.rs @@ -263,15 +263,9 @@ impl Session { drop(active); if !pending_input.is_empty() { for pending_input_item in pending_input { - match inspect_pending_input(self, &turn_context, pending_input_item.input).await { + match inspect_pending_input(self, &turn_context, pending_input_item).await { PendingInputHookDisposition::Accepted(pending_input) => { - record_pending_input( - self, - &turn_context, - *pending_input, - pending_input_item.metadata, - ) - .await; + record_pending_input(self, &turn_context, *pending_input).await; } PendingInputHookDisposition::Blocked { additional_contexts,