Add ItemStarted/ItemCompleted events for UserInputItem (#5306)

Adds a new ItemStarted event and delivers UserMessage as the first item
type (more to come).


Renames `InputItem` to `UserInput` considering we're using the `Item`
suffix for actual items.
This commit is contained in:
pakrym-oai
2025-10-20 13:34:44 -07:00
committed by GitHub
parent 5e4f3bbb0b
commit 9c903c4716
49 changed files with 435 additions and 202 deletions

View File

@@ -10,12 +10,15 @@ use crate::event_mapping::map_response_item_to_event_messages;
use crate::function_tool::FunctionCallError;
use crate::parse_command::parse_command;
use crate::review_format::format_review_findings_block;
use crate::state::ItemCollector;
use crate::terminal;
use crate::user_notification::UserNotifier;
use async_channel::Receiver;
use async_channel::Sender;
use codex_apply_patch::ApplyPatchAction;
use codex_protocol::ConversationId;
use codex_protocol::items::TurnItem;
use codex_protocol::items::UserMessageItem;
use codex_protocol::protocol::ConversationPathResponseEvent;
use codex_protocol::protocol::ExitedReviewModeEvent;
use codex_protocol::protocol::McpAuthStatus;
@@ -77,7 +80,6 @@ use crate::protocol::ErrorEvent;
use crate::protocol::Event;
use crate::protocol::EventMsg;
use crate::protocol::ExecApprovalRequestEvent;
use crate::protocol::InputItem;
use crate::protocol::ListCustomPromptsResponseEvent;
use crate::protocol::Op;
use crate::protocol::RateLimitSnapshot;
@@ -122,6 +124,7 @@ use codex_protocol::models::FunctionCallOutputPayload;
use codex_protocol::models::ResponseInputItem;
use codex_protocol::models::ResponseItem;
use codex_protocol::protocol::InitialHistory;
use codex_protocol::user_input::UserInput;
pub mod compact;
use self::compact::build_compacted_history;
@@ -264,6 +267,7 @@ pub(crate) struct TurnContext {
pub(crate) is_review_mode: bool,
pub(crate) final_output_json_schema: Option<Value>,
pub(crate) codex_linux_sandbox_exe: Option<PathBuf>,
pub(crate) item_collector: ItemCollector,
}
impl TurnContext {
@@ -352,6 +356,7 @@ impl Session {
provider: ModelProviderInfo,
session_configuration: &SessionConfiguration,
conversation_id: ConversationId,
tx_event: Sender<Event>,
) -> TurnContext {
let config = session_configuration.original_config_do_not_use.clone();
let model_family = find_family_for_model(&session_configuration.model)
@@ -397,6 +402,7 @@ impl Session {
is_review_mode: false,
final_output_json_schema: None,
codex_linux_sandbox_exe: config.codex_linux_sandbox_exe.clone(),
item_collector: ItemCollector::new(tx_event, conversation_id, "turn_id".to_string()),
}
}
@@ -656,6 +662,7 @@ impl Session {
session_configuration.provider.clone(),
&session_configuration,
self.conversation_id,
self.get_tx_event(),
);
if let Some(final_schema) = updates.final_output_json_schema {
turn_context.final_output_json_schema = final_schema;
@@ -986,7 +993,7 @@ impl Session {
}
/// Returns the input if there was no task running to inject into
pub async fn inject_input(&self, input: Vec<InputItem>) -> Result<(), Vec<InputItem>> {
pub async fn inject_input(&self, input: Vec<UserInput>) -> Result<(), Vec<UserInput>> {
let mut active = self.active_turn.lock().await;
match active.as_mut() {
Some(at) => {
@@ -1157,6 +1164,11 @@ async fn submission_loop(sess: Arc<Session>, config: Arc<Config>, rx_sub: Receiv
}
}
current_context
.item_collector
.started_completed(TurnItem::UserMessage(UserMessageItem::new(&items)))
.await;
sess.spawn_task(Arc::clone(&current_context), sub.id, items, RegularTask)
.await;
previous_context = Some(current_context);
@@ -1268,7 +1280,7 @@ async fn submission_loop(sess: Arc<Session>, config: Arc<Config>, rx_sub: Receiv
let turn_context = sess.new_turn(SessionSettingsUpdate::default()).await;
// Attempt to inject input into current task
if let Err(items) = sess
.inject_input(vec![InputItem::Text {
.inject_input(vec![UserInput::Text {
text: compact::SUMMARIZATION_PROMPT.to_string(),
}])
.await
@@ -1422,10 +1434,15 @@ async fn spawn_review_thread(
is_review_mode: true,
final_output_json_schema: None,
codex_linux_sandbox_exe: parent_turn_context.codex_linux_sandbox_exe.clone(),
item_collector: ItemCollector::new(
sess.get_tx_event(),
sess.conversation_id,
sub_id.to_string(),
),
};
// Seed the child task with the review prompt as the initial user message.
let input: Vec<InputItem> = vec![InputItem::Text {
let input: Vec<UserInput> = vec![UserInput::Text {
text: review_prompt,
}];
let tc = Arc::new(review_turn_context);
@@ -1463,7 +1480,7 @@ pub(crate) async fn run_task(
sess: Arc<Session>,
turn_context: Arc<TurnContext>,
sub_id: String,
input: Vec<InputItem>,
input: Vec<UserInput>,
task_kind: TaskKind,
cancellation_token: CancellationToken,
) -> Option<String> {
@@ -2624,6 +2641,15 @@ mod tests {
tool_approvals: Mutex::new(ApprovalStore::default()),
};
let turn_context = Session::make_turn_context(
Some(Arc::clone(&auth_manager)),
&otel_event_manager,
session_configuration.provider.clone(),
&session_configuration,
conversation_id,
tx_event.clone(),
);
let session = Session {
conversation_id,
tx_event,
@@ -2633,13 +2659,6 @@ mod tests {
next_internal_sub_id: AtomicU64::new(0),
};
let turn_context = Session::make_turn_context(
Some(Arc::clone(&auth_manager)),
&otel_event_manager,
session_configuration.provider.clone(),
&session_configuration,
conversation_id,
);
(session, turn_context)
}
@@ -2690,6 +2709,15 @@ mod tests {
tool_approvals: Mutex::new(ApprovalStore::default()),
};
let turn_context = Arc::new(Session::make_turn_context(
Some(Arc::clone(&auth_manager)),
&otel_event_manager,
session_configuration.provider.clone(),
&session_configuration,
conversation_id,
tx_event.clone(),
));
let session = Arc::new(Session {
conversation_id,
tx_event,
@@ -2699,13 +2727,6 @@ mod tests {
next_internal_sub_id: AtomicU64::new(0),
});
let turn_context = Arc::new(Session::make_turn_context(
Some(Arc::clone(&auth_manager)),
&otel_event_manager,
session_configuration.provider.clone(),
&session_configuration,
conversation_id,
));
(session, turn_context, rx_event)
}
@@ -2726,7 +2747,7 @@ mod tests {
_session: Arc<SessionTaskContext>,
_ctx: Arc<TurnContext>,
_sub_id: String,
_input: Vec<InputItem>,
_input: Vec<UserInput>,
cancellation_token: CancellationToken,
) -> Option<String> {
if self.listen_to_cancellation_token {
@@ -2750,7 +2771,7 @@ mod tests {
async fn abort_regular_task_emits_turn_aborted_only() {
let (sess, tc, rx) = make_session_and_context_with_rx();
let sub_id = "sub-regular".to_string();
let input = vec![InputItem::Text {
let input = vec![UserInput::Text {
text: "hello".to_string(),
}];
sess.spawn_task(
@@ -2781,7 +2802,7 @@ mod tests {
async fn abort_gracefuly_emits_turn_aborted_only() {
let (sess, tc, rx) = make_session_and_context_with_rx();
let sub_id = "sub-regular".to_string();
let input = vec![InputItem::Text {
let input = vec![UserInput::Text {
text: "hello".to_string(),
}];
sess.spawn_task(
@@ -2809,7 +2830,7 @@ mod tests {
async fn abort_review_task_emits_exited_then_aborted_and_records_history() {
let (sess, tc, rx) = make_session_and_context_with_rx();
let sub_id = "sub-review".to_string();
let input = vec![InputItem::Text {
let input = vec![UserInput::Text {
text: "start review".to_string(),
}];
sess.spawn_task(

View File

@@ -12,7 +12,6 @@ use crate::protocol::CompactedItem;
use crate::protocol::ErrorEvent;
use crate::protocol::Event;
use crate::protocol::EventMsg;
use crate::protocol::InputItem;
use crate::protocol::InputMessageKind;
use crate::protocol::TaskStartedEvent;
use crate::protocol::TurnContextItem;
@@ -24,6 +23,7 @@ use codex_protocol::models::ContentItem;
use codex_protocol::models::ResponseInputItem;
use codex_protocol::models::ResponseItem;
use codex_protocol::protocol::RolloutItem;
use codex_protocol::user_input::UserInput;
use futures::prelude::*;
pub const SUMMARIZATION_PROMPT: &str = include_str!("../../templates/compact/prompt.md");
@@ -41,7 +41,7 @@ pub(crate) async fn run_inline_auto_compact_task(
turn_context: Arc<TurnContext>,
) {
let sub_id = sess.next_internal_sub_id();
let input = vec![InputItem::Text {
let input = vec![UserInput::Text {
text: SUMMARIZATION_PROMPT.to_string(),
}];
run_compact_task_inner(sess, turn_context, sub_id, input).await;
@@ -51,7 +51,7 @@ pub(crate) async fn run_compact_task(
sess: Arc<Session>,
turn_context: Arc<TurnContext>,
sub_id: String,
input: Vec<InputItem>,
input: Vec<UserInput>,
) -> Option<String> {
let start_event = Event {
id: sub_id.clone(),
@@ -68,7 +68,7 @@ async fn run_compact_task_inner(
sess: Arc<Session>,
turn_context: Arc<TurnContext>,
sub_id: String,
input: Vec<InputItem>,
input: Vec<UserInput>,
) {
let initial_input_for_turn: ResponseInputItem = ResponseInputItem::from(input);
let mut turn_input = sess

View File

@@ -71,6 +71,8 @@ pub(crate) fn should_persist_event_msg(ev: &EventMsg) -> bool {
| EventMsg::PlanUpdate(_)
| EventMsg::ShutdownComplete
| EventMsg::ViewImageToolCall(_)
| EventMsg::ConversationPath(_) => false,
| EventMsg::ConversationPath(_)
| EventMsg::ItemStarted(_)
| EventMsg::ItemCompleted(_) => false,
}
}

View File

@@ -0,0 +1,68 @@
use async_channel::Sender;
use codex_protocol::ConversationId;
use codex_protocol::items::TurnItem;
use codex_protocol::protocol::Event;
use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::ItemCompletedEvent;
use codex_protocol::protocol::ItemStartedEvent;
use tracing::error;
#[derive(Debug)]
pub(crate) struct ItemCollector {
thread_id: ConversationId,
turn_id: String,
tx_event: Sender<Event>,
}
impl ItemCollector {
pub fn new(
tx_event: Sender<Event>,
thread_id: ConversationId,
turn_id: String,
) -> ItemCollector {
ItemCollector {
tx_event,
thread_id,
turn_id,
}
}
pub async fn started(&self, item: TurnItem) {
let err = self
.tx_event
.send(Event {
id: self.turn_id.clone(),
msg: EventMsg::ItemStarted(ItemStartedEvent {
thread_id: self.thread_id,
turn_id: self.turn_id.clone(),
item,
}),
})
.await;
if let Err(e) = err {
error!("failed to send item started event: {e}");
}
}
pub async fn completed(&self, item: TurnItem) {
let err = self
.tx_event
.send(Event {
id: self.turn_id.clone(),
msg: EventMsg::ItemCompleted(ItemCompletedEvent {
thread_id: self.thread_id,
turn_id: self.turn_id.clone(),
item,
}),
})
.await;
if let Err(e) = err {
error!("failed to send item completed event: {e}");
}
}
pub async fn started_completed(&self, item: TurnItem) {
self.started(item.clone()).await;
self.completed(item).await;
}
}

View File

@@ -1,7 +1,9 @@
mod item_collector;
mod service;
mod session;
mod turn;
pub(crate) use item_collector::ItemCollector;
pub(crate) use service::SessionServices;
pub(crate) use session::SessionState;
pub(crate) use turn::ActiveTurn;

View File

@@ -5,8 +5,8 @@ use tokio_util::sync::CancellationToken;
use crate::codex::TurnContext;
use crate::codex::compact;
use crate::protocol::InputItem;
use crate::state::TaskKind;
use codex_protocol::user_input::UserInput;
use super::SessionTask;
use super::SessionTaskContext;
@@ -25,7 +25,7 @@ impl SessionTask for CompactTask {
session: Arc<SessionTaskContext>,
ctx: Arc<TurnContext>,
sub_id: String,
input: Vec<InputItem>,
input: Vec<UserInput>,
_cancellation_token: CancellationToken,
) -> Option<String> {
compact::run_compact_task(session.clone_session(), ctx, sub_id, input).await

View File

@@ -17,13 +17,13 @@ use crate::codex::Session;
use crate::codex::TurnContext;
use crate::protocol::Event;
use crate::protocol::EventMsg;
use crate::protocol::InputItem;
use crate::protocol::TaskCompleteEvent;
use crate::protocol::TurnAbortReason;
use crate::protocol::TurnAbortedEvent;
use crate::state::ActiveTurn;
use crate::state::RunningTask;
use crate::state::TaskKind;
use codex_protocol::user_input::UserInput;
pub(crate) use compact::CompactTask;
pub(crate) use regular::RegularTask;
@@ -56,7 +56,7 @@ pub(crate) trait SessionTask: Send + Sync + 'static {
session: Arc<SessionTaskContext>,
ctx: Arc<TurnContext>,
sub_id: String,
input: Vec<InputItem>,
input: Vec<UserInput>,
cancellation_token: CancellationToken,
) -> Option<String>;
@@ -70,7 +70,7 @@ impl Session {
self: &Arc<Self>,
turn_context: Arc<TurnContext>,
sub_id: String,
input: Vec<InputItem>,
input: Vec<UserInput>,
task: T,
) {
self.abort_all_tasks(TurnAbortReason::Replaced).await;

View File

@@ -5,8 +5,8 @@ use tokio_util::sync::CancellationToken;
use crate::codex::TurnContext;
use crate::codex::run_task;
use crate::protocol::InputItem;
use crate::state::TaskKind;
use codex_protocol::user_input::UserInput;
use super::SessionTask;
use super::SessionTaskContext;
@@ -25,7 +25,7 @@ impl SessionTask for RegularTask {
session: Arc<SessionTaskContext>,
ctx: Arc<TurnContext>,
sub_id: String,
input: Vec<InputItem>,
input: Vec<UserInput>,
cancellation_token: CancellationToken,
) -> Option<String> {
let sess = session.clone_session();

View File

@@ -6,8 +6,8 @@ use tokio_util::sync::CancellationToken;
use crate::codex::TurnContext;
use crate::codex::exit_review_mode;
use crate::codex::run_task;
use crate::protocol::InputItem;
use crate::state::TaskKind;
use codex_protocol::user_input::UserInput;
use super::SessionTask;
use super::SessionTaskContext;
@@ -26,7 +26,7 @@ impl SessionTask for ReviewTask {
session: Arc<SessionTaskContext>,
ctx: Arc<TurnContext>,
sub_id: String,
input: Vec<InputItem>,
input: Vec<UserInput>,
cancellation_token: CancellationToken,
) -> Option<String> {
let sess = session.clone_session();

View File

@@ -5,13 +5,13 @@ use tokio::fs;
use crate::function_tool::FunctionCallError;
use crate::protocol::Event;
use crate::protocol::EventMsg;
use crate::protocol::InputItem;
use crate::protocol::ViewImageToolCallEvent;
use crate::tools::context::ToolInvocation;
use crate::tools::context::ToolOutput;
use crate::tools::context::ToolPayload;
use crate::tools::registry::ToolHandler;
use crate::tools::registry::ToolKind;
use codex_protocol::user_input::UserInput;
pub struct ViewImageHandler;
@@ -67,7 +67,7 @@ impl ToolHandler for ViewImageHandler {
let event_path = abs_path.clone();
session
.inject_input(vec![InputItem::LocalImage { path: abs_path }])
.inject_input(vec![UserInput::LocalImage { path: abs_path }])
.await
.map_err(|_| {
FunctionCallError::RespondToModel(

View File

@@ -134,6 +134,14 @@ where
wait_for_event_with_timeout(codex, predicate, Duration::from_secs(1)).await
}
pub async fn wait_for_event_match<T, F>(codex: &CodexConversation, matcher: F) -> T
where
F: Fn(&codex_core::protocol::EventMsg) -> Option<T>,
{
let ev = wait_for_event(codex, |ev| matcher(ev).is_some()).await;
matcher(&ev).unwrap()
}
pub async fn wait_for_event_with_timeout<F>(
codex: &CodexConversation,
mut predicate: F,

View File

@@ -1,8 +1,8 @@
use std::time::Duration;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_protocol::user_input::UserInput;
use core_test_support::responses::ev_completed;
use core_test_support::responses::ev_function_call;
use core_test_support::responses::mount_sse_once;
@@ -42,7 +42,7 @@ async fn interrupt_long_running_tool_emits_turn_aborted() {
// Kick off a turn that triggers the function call.
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "start sleep".into(),
}],
})

View File

@@ -6,11 +6,11 @@ use codex_core::protocol::ApplyPatchApprovalRequestEvent;
use codex_core::protocol::AskForApproval;
use codex_core::protocol::EventMsg;
use codex_core::protocol::ExecApprovalRequestEvent;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_core::protocol::SandboxPolicy;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::protocol::ReviewDecision;
use codex_protocol::user_input::UserInput;
use core_test_support::responses::ev_apply_patch_function_call;
use core_test_support::responses::ev_assistant_message;
use core_test_support::responses::ev_completed;
@@ -374,7 +374,7 @@ async fn submit_turn(
test.codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: prompt.into(),
}],
final_output_json_schema: None,

View File

@@ -17,13 +17,13 @@ use codex_core::built_in_model_providers;
use codex_core::error::CodexErr;
use codex_core::model_family::find_family_for_model;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_core::protocol::SessionSource;
use codex_otel::otel_event_manager::OtelEventManager;
use codex_protocol::ConversationId;
use codex_protocol::models::ReasoningItemReasoningSummary;
use codex_protocol::models::WebSearchAction;
use codex_protocol::user_input::UserInput;
use core_test_support::load_default_config_for_test;
use core_test_support::load_sse_fixture_with_id;
use core_test_support::responses;
@@ -263,7 +263,7 @@ async fn resume_includes_initial_messages_and_sends_prior_items() {
// 2) Submit new input; the request body must include the prior item followed by the new user input.
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -335,7 +335,7 @@ async fn includes_conversation_id_and_model_headers_in_request() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -390,7 +390,7 @@ async fn includes_base_instructions_override_in_request() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -450,7 +450,7 @@ async fn chatgpt_auth_sends_correct_request() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -540,7 +540,7 @@ async fn prefers_apikey_when_config_prefers_apikey_even_with_chatgpt_tokens() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -579,7 +579,7 @@ async fn includes_user_instructions_message_in_request() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -795,7 +795,7 @@ async fn token_count_includes_rate_limits_snapshot() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -946,7 +946,7 @@ async fn usage_limit_error_emits_rate_limit_event() -> anyhow::Result<()> {
let submission_id = codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -1016,7 +1016,7 @@ async fn context_window_error_sets_total_tokens_to_model_window() -> anyhow::Res
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "seed turn".into(),
}],
})
@@ -1026,7 +1026,7 @@ async fn context_window_error_sets_total_tokens_to_model_window() -> anyhow::Res
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "trigger context window".into(),
}],
})
@@ -1146,7 +1146,7 @@ async fn azure_overrides_assign_properties_used_for_responses_url() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -1223,7 +1223,7 @@ async fn env_var_overrides_loaded_auth() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -1301,7 +1301,7 @@ async fn history_dedupes_streamed_and_final_messages_across_turns() {
// Turn 1: user sends U1; wait for completion.
codex
.submit(Op::UserInput {
items: vec![InputItem::Text { text: "U1".into() }],
items: vec![UserInput::Text { text: "U1".into() }],
})
.await
.unwrap();
@@ -1310,7 +1310,7 @@ async fn history_dedupes_streamed_and_final_messages_across_turns() {
// Turn 2: user sends U2; wait for completion.
codex
.submit(Op::UserInput {
items: vec![InputItem::Text { text: "U2".into() }],
items: vec![UserInput::Text { text: "U2".into() }],
})
.await
.unwrap();
@@ -1319,7 +1319,7 @@ async fn history_dedupes_streamed_and_final_messages_across_turns() {
// Turn 3: user sends U3; wait for completion.
codex
.submit(Op::UserInput {
items: vec![InputItem::Text { text: "U3".into() }],
items: vec![UserInput::Text { text: "U3".into() }],
})
.await
.unwrap();

View File

@@ -5,10 +5,10 @@ use codex_core::NewConversation;
use codex_core::built_in_model_providers;
use codex_core::protocol::ErrorEvent;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_core::protocol::RolloutItem;
use codex_core::protocol::RolloutLine;
use codex_protocol::user_input::UserInput;
use core_test_support::load_default_config_for_test;
use core_test_support::skip_if_no_network;
use core_test_support::wait_for_event;
@@ -108,7 +108,7 @@ async fn summarize_context_three_requests_and_instructions() {
// 1) Normal user input should hit server once.
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello world".into(),
}],
})
@@ -123,7 +123,7 @@ async fn summarize_context_three_requests_and_instructions() {
// 3) Next user input third hit; history should include only the summary.
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: THIRD_USER_MSG.into(),
}],
})
@@ -324,7 +324,7 @@ async fn auto_compact_runs_after_token_limit_hit() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: FIRST_AUTO_MSG.into(),
}],
})
@@ -335,7 +335,7 @@ async fn auto_compact_runs_after_token_limit_hit() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: SECOND_AUTO_MSG.into(),
}],
})
@@ -468,7 +468,7 @@ async fn auto_compact_persists_rollout_entries() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: FIRST_AUTO_MSG.into(),
}],
})
@@ -478,7 +478,7 @@ async fn auto_compact_persists_rollout_entries() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: SECOND_AUTO_MSG.into(),
}],
})
@@ -580,7 +580,7 @@ async fn auto_compact_stops_after_failed_attempt() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: FIRST_AUTO_MSG.into(),
}],
})
@@ -674,7 +674,7 @@ async fn manual_compact_retries_after_context_window_error() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "first turn".into(),
}],
})
@@ -802,7 +802,7 @@ async fn auto_compact_allows_multiple_attempts_when_interleaved_with_other_turn_
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: MULTI_AUTO_MSG.into(),
}],
})
@@ -913,7 +913,7 @@ async fn auto_compact_triggers_after_function_call_over_95_percent_usage() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: FUNCTION_CALL_LIMIT_MSG.into(),
}],
})

View File

@@ -20,9 +20,9 @@ use codex_core::config::Config;
use codex_core::config::OPENAI_DEFAULT_MODEL;
use codex_core::protocol::ConversationPathResponseEvent;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_core::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR;
use codex_protocol::user_input::UserInput;
use core_test_support::load_default_config_for_test;
use core_test_support::responses::ev_assistant_message;
use core_test_support::responses::ev_completed;
@@ -777,7 +777,7 @@ async fn start_test_conversation(
async fn user_turn(conversation: &Arc<CodexConversation>, text: &str) {
conversation
.submit(Op::UserInput {
items: vec![InputItem::Text { text: text.into() }],
items: vec![UserInput::Text { text: text.into() }],
})
.await
.expect("submit user turn");

View File

@@ -9,10 +9,10 @@ use codex_core::content_items_to_text;
use codex_core::is_session_prefix_message;
use codex_core::protocol::ConversationPathResponseEvent;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_core::protocol::RolloutItem;
use codex_core::protocol::RolloutLine;
use codex_protocol::user_input::UserInput;
use core_test_support::load_default_config_for_test;
use core_test_support::skip_if_no_network;
use core_test_support::wait_for_event;
@@ -71,7 +71,7 @@ async fn fork_conversation_twice_drops_to_first_message() {
for text in ["first", "second", "third"] {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: text.to_string(),
}],
})

View File

@@ -4,10 +4,10 @@ use anyhow::Result;
use codex_core::model_family::find_family_for_model;
use codex_core::protocol::AskForApproval;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_core::protocol::SandboxPolicy;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::user_input::UserInput;
use core_test_support::responses;
use core_test_support::responses::ev_assistant_message;
use core_test_support::responses::ev_completed;
@@ -149,7 +149,7 @@ async fn submit_turn(test: &TestCodex, prompt: &str) -> Result<()> {
test.codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: prompt.into(),
}],
final_output_json_schema: None,

View File

@@ -0,0 +1,68 @@
#![cfg(not(target_os = "windows"))]
use anyhow::Ok;
use codex_core::protocol::EventMsg;
use codex_core::protocol::Op;
use codex_protocol::items::TurnItem;
use codex_protocol::user_input::UserInput;
use core_test_support::responses;
use core_test_support::responses::ev_completed;
use core_test_support::responses::ev_response_created;
use core_test_support::responses::sse;
use core_test_support::responses::start_mock_server;
use core_test_support::skip_if_no_network;
use core_test_support::test_codex::TestCodex;
use core_test_support::test_codex::test_codex;
use core_test_support::wait_for_event_match;
use pretty_assertions::assert_eq;
use wiremock::matchers::any;
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn user_message_item_is_emitted() -> anyhow::Result<()> {
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let TestCodex { codex, .. } = test_codex().build(&server).await?;
let first_response = sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]);
responses::mount_sse_once_match(&server, any(), first_response).await;
codex
.submit(Op::UserInput {
items: (vec![UserInput::Text {
text: "please inspect sample.txt".into(),
}]),
})
.await?;
let started = wait_for_event_match(&codex, |ev| match ev {
EventMsg::ItemStarted(e) => Some(e.clone()),
_ => None,
})
.await;
let completed = wait_for_event_match(&codex, |ev| match ev {
EventMsg::ItemCompleted(e) => Some(e.clone()),
_ => None,
})
.await;
let TurnItem::UserMessage(started_item) = started.item;
let TurnItem::UserMessage(completed_item) = completed.item;
assert_eq!(started_item.id, completed_item.id);
assert_eq!(
started_item.content,
vec![UserInput::Text {
text: "please inspect sample.txt".into(),
}]
);
assert_eq!(
completed_item.content,
vec![UserInput::Text {
text: "please inspect sample.txt".into(),
}]
);
Ok(())
}

View File

@@ -2,10 +2,10 @@
use codex_core::protocol::AskForApproval;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_core::protocol::SandboxPolicy;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::user_input::UserInput;
use core_test_support::responses;
use core_test_support::skip_if_no_network;
use core_test_support::test_codex::TestCodex;
@@ -74,7 +74,7 @@ async fn codex_returns_json_result(model: String) -> anyhow::Result<()> {
// 1) Normal user input should hit server once.
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello world".into(),
}],
final_output_json_schema: Some(serde_json::from_str(SCHEMA)?),

View File

@@ -2,10 +2,10 @@
use codex_core::protocol::AskForApproval;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_core::protocol::SandboxPolicy;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::user_input::UserInput;
use core_test_support::responses;
use core_test_support::responses::ev_assistant_message;
use core_test_support::responses::ev_completed;
@@ -66,7 +66,7 @@ async fn list_dir_tool_returns_entries() -> anyhow::Result<()> {
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "list directory contents".into(),
}],
final_output_json_schema: None,
@@ -171,7 +171,7 @@ async fn list_dir_tool_depth_one_omits_children() -> anyhow::Result<()> {
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "list directory contents depth one".into(),
}],
final_output_json_schema: None,
@@ -283,7 +283,7 @@ async fn list_dir_tool_depth_two_includes_children_only() -> anyhow::Result<()>
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "list directory contents depth two".into(),
}],
final_output_json_schema: None,
@@ -398,7 +398,7 @@ async fn list_dir_tool_depth_three_includes_grandchildren() -> anyhow::Result<()
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "list directory contents depth three".into(),
}],
final_output_json_schema: None,

View File

@@ -11,6 +11,7 @@ mod compact_resume_fork;
mod exec;
mod fork_conversation;
mod grep_files;
mod items;
mod json_result;
mod list_dir;
mod live_cli;

View File

@@ -7,8 +7,8 @@ use codex_core::built_in_model_providers;
use codex_core::features::Feature;
use codex_core::model_family::find_family_for_model;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_protocol::user_input::UserInput;
use core_test_support::load_default_config_for_test;
use core_test_support::load_sse_fixture_with_id;
use core_test_support::responses;
@@ -74,7 +74,7 @@ async fn collect_tool_identifiers_for_model(model: &str) -> Vec<String> {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello tools".into(),
}],
})

View File

@@ -1,9 +1,9 @@
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::InputItem;
use codex_protocol::protocol::Op;
use codex_protocol::protocol::ReviewDecision;
use codex_protocol::protocol::SandboxPolicy;
use codex_protocol::user_input::UserInput;
use core_test_support::responses::ev_assistant_message;
use core_test_support::responses::ev_completed;
use core_test_support::responses::ev_custom_tool_call;
@@ -31,7 +31,7 @@ async fn responses_api_emits_api_request_event() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -77,7 +77,7 @@ async fn process_sse_emits_tracing_for_output_item() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -121,7 +121,7 @@ async fn process_sse_emits_failed_event_on_parse_error() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -166,7 +166,7 @@ async fn process_sse_records_failed_event_when_stream_closes_without_completed()
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -223,7 +223,7 @@ async fn process_sse_failed_event_records_response_error_message() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -278,7 +278,7 @@ async fn process_sse_failed_event_logs_parse_error() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -328,7 +328,7 @@ async fn process_sse_failed_event_logs_missing_error() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -378,7 +378,7 @@ async fn process_sse_failed_event_logs_response_completed_parse_error() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -433,7 +433,7 @@ async fn process_sse_emits_completed_telemetry() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -493,7 +493,7 @@ async fn handle_response_item_records_tool_result_for_custom_tool_call() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -557,7 +557,7 @@ async fn handle_response_item_records_tool_result_for_function_call() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -631,7 +631,7 @@ async fn handle_response_item_records_tool_result_for_local_shell_missing_ids()
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -689,7 +689,7 @@ async fn handle_response_item_records_tool_result_for_local_shell_call() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -787,7 +787,7 @@ async fn handle_container_exec_autoapprove_from_config_records_tool_decision() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})
@@ -833,7 +833,7 @@ async fn handle_container_exec_user_approved_records_tool_decision() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "approved".into(),
}],
})
@@ -895,7 +895,7 @@ async fn handle_container_exec_user_approved_for_session_records_tool_decision()
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "persist".into(),
}],
})
@@ -957,7 +957,7 @@ async fn handle_sandbox_error_user_approves_retry_records_tool_decision() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "retry".into(),
}],
})
@@ -1019,7 +1019,7 @@ async fn handle_container_exec_user_denies_records_tool_decision() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "deny".into(),
}],
})
@@ -1081,7 +1081,7 @@ async fn handle_sandbox_error_user_approves_for_session_records_tool_decision()
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "persist".into(),
}],
})
@@ -1143,7 +1143,7 @@ async fn handle_sandbox_error_user_denies_records_tool_decision() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "deny".into(),
}],
})

View File

@@ -9,13 +9,13 @@ use codex_core::features::Feature;
use codex_core::model_family::find_family_for_model;
use codex_core::protocol::AskForApproval;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_core::protocol::SandboxPolicy;
use codex_core::protocol_config_types::ReasoningEffort;
use codex_core::protocol_config_types::ReasoningSummary;
use codex_core::shell::Shell;
use codex_core::shell::default_user_shell;
use codex_protocol::user_input::UserInput;
use core_test_support::load_default_config_for_test;
use core_test_support::load_sse_fixture_with_id;
use core_test_support::skip_if_no_network;
@@ -115,7 +115,7 @@ async fn codex_mini_latest_tools() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello 1".into(),
}],
})
@@ -125,7 +125,7 @@ async fn codex_mini_latest_tools() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello 2".into(),
}],
})
@@ -199,7 +199,7 @@ async fn prompt_tools_are_consistent_across_requests() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello 1".into(),
}],
})
@@ -209,7 +209,7 @@ async fn prompt_tools_are_consistent_across_requests() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello 2".into(),
}],
})
@@ -319,7 +319,7 @@ async fn prefixes_context_and_instructions_once_and_consistently_across_requests
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello 1".into(),
}],
})
@@ -329,7 +329,7 @@ async fn prefixes_context_and_instructions_once_and_consistently_across_requests
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello 2".into(),
}],
})
@@ -439,7 +439,7 @@ async fn overrides_turn_context_but_keeps_cached_prefix_and_key_constant() {
// First turn
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello 1".into(),
}],
})
@@ -468,7 +468,7 @@ async fn overrides_turn_context_but_keeps_cached_prefix_and_key_constant() {
// Second turn after overrides
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello 2".into(),
}],
})
@@ -567,7 +567,7 @@ async fn per_turn_overrides_keep_cached_prefix_and_key_constant() {
// First turn
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello 1".into(),
}],
})
@@ -580,7 +580,7 @@ async fn per_turn_overrides_keep_cached_prefix_and_key_constant() {
let writable = TempDir::new().unwrap();
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello 2".into(),
}],
cwd: new_cwd.path().to_path_buf(),
@@ -696,7 +696,7 @@ async fn send_user_turn_with_no_changes_does_not_send_environment_context() {
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello 1".into(),
}],
cwd: default_cwd.clone(),
@@ -713,7 +713,7 @@ async fn send_user_turn_with_no_changes_does_not_send_environment_context() {
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello 2".into(),
}],
cwd: default_cwd.clone(),
@@ -810,7 +810,7 @@ async fn send_user_turn_with_changes_sends_environment_context() {
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello 1".into(),
}],
cwd: default_cwd.clone(),
@@ -827,7 +827,7 @@ async fn send_user_turn_with_changes_sends_environment_context() {
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello 2".into(),
}],
cwd: default_cwd.clone(),

View File

@@ -2,10 +2,10 @@
use codex_core::protocol::AskForApproval;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_core::protocol::SandboxPolicy;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::user_input::UserInput;
use core_test_support::responses;
use core_test_support::responses::ev_assistant_message;
use core_test_support::responses::ev_completed;
@@ -64,7 +64,7 @@ async fn read_file_tool_returns_requested_lines() -> anyhow::Result<()> {
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "please inspect sample.txt".into(),
}],
final_output_json_schema: None,

View File

@@ -11,7 +11,6 @@ use codex_core::protocol::ConversationPathResponseEvent;
use codex_core::protocol::ENVIRONMENT_CONTEXT_OPEN_TAG;
use codex_core::protocol::EventMsg;
use codex_core::protocol::ExitedReviewModeEvent;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_core::protocol::ReviewCodeLocation;
use codex_core::protocol::ReviewFinding;
@@ -20,6 +19,7 @@ use codex_core::protocol::ReviewOutputEvent;
use codex_core::protocol::ReviewRequest;
use codex_core::protocol::RolloutItem;
use codex_core::protocol::RolloutLine;
use codex_protocol::user_input::UserInput;
use core_test_support::load_default_config_for_test;
use core_test_support::load_sse_fixture_with_id_from_str;
use core_test_support::skip_if_no_network;
@@ -566,7 +566,7 @@ async fn review_history_does_not_leak_into_parent_session() {
let followup = "back to parent".to_string();
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: followup.clone(),
}],
})

View File

@@ -14,10 +14,10 @@ use codex_core::features::Feature;
use codex_core::protocol::AskForApproval;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_core::protocol::SandboxPolicy;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::user_input::UserInput;
use core_test_support::responses;
use core_test_support::responses::mount_sse_once_match;
use core_test_support::skip_if_no_network;
@@ -104,7 +104,7 @@ async fn stdio_server_round_trip() -> anyhow::Result<()> {
fixture
.codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "call the rmcp echo tool".into(),
}],
final_output_json_schema: None,
@@ -240,7 +240,7 @@ async fn stdio_server_propagates_whitelisted_env_vars() -> anyhow::Result<()> {
fixture
.codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "call the rmcp echo tool".into(),
}],
final_output_json_schema: None,
@@ -391,7 +391,7 @@ async fn streamable_http_tool_call_round_trip() -> anyhow::Result<()> {
fixture
.codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "call the rmcp streamable http echo tool".into(),
}],
final_output_json_schema: None,
@@ -574,7 +574,7 @@ async fn streamable_http_with_oauth_round_trip() -> anyhow::Result<()> {
fixture
.codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "call the rmcp streamable http oauth echo tool".into(),
}],
final_output_json_schema: None,

View File

@@ -5,10 +5,10 @@ use codex_core::features::Feature;
use codex_core::model_family::find_family_for_model;
use codex_core::protocol::AskForApproval;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_core::protocol::SandboxPolicy;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::user_input::UserInput;
use core_test_support::assert_regex_match;
use core_test_support::responses::ev_apply_patch_function_call;
use core_test_support::responses::ev_assistant_message;
@@ -35,7 +35,7 @@ async fn submit_turn(test: &TestCodex, prompt: &str, sandbox_policy: SandboxPoli
test.codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: prompt.into(),
}],
final_output_json_schema: None,

View File

@@ -3,8 +3,8 @@ use std::time::Duration;
use codex_core::ModelProviderInfo;
use codex_core::WireApi;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_protocol::user_input::UserInput;
use core_test_support::load_sse_fixture_with_id;
use core_test_support::skip_if_no_network;
use core_test_support::test_codex::TestCodex;
@@ -87,7 +87,7 @@ async fn continue_after_stream_error() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "first message".into(),
}],
})
@@ -114,7 +114,7 @@ async fn continue_after_stream_error() {
// error above, this submission would be rejected/queued indefinitely.
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "follow up".into(),
}],
})

View File

@@ -6,8 +6,8 @@ use std::time::Duration;
use codex_core::ModelProviderInfo;
use codex_core::WireApi;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_protocol::user_input::UserInput;
use core_test_support::load_sse_fixture;
use core_test_support::load_sse_fixture_with_id;
use core_test_support::skip_if_no_network;
@@ -94,7 +94,7 @@ async fn retries_on_early_close() {
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello".into(),
}],
})

View File

@@ -7,11 +7,11 @@ use codex_core::features::Feature;
use codex_core::model_family::find_family_for_model;
use codex_core::protocol::AskForApproval;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_core::protocol::SandboxPolicy;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::plan_tool::StepStatus;
use codex_protocol::user_input::UserInput;
use core_test_support::assert_regex_match;
use core_test_support::responses;
use core_test_support::responses::ev_apply_patch_function_call;
@@ -74,7 +74,7 @@ async fn shell_tool_executes_command_and_streams_output() -> anyhow::Result<()>
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "please run the shell command".into(),
}],
final_output_json_schema: None,
@@ -143,7 +143,7 @@ async fn update_plan_tool_emits_plan_update_event() -> anyhow::Result<()> {
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "please update the plan".into(),
}],
final_output_json_schema: None,
@@ -226,7 +226,7 @@ async fn update_plan_tool_rejects_malformed_payload() -> anyhow::Result<()> {
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "please update the plan".into(),
}],
final_output_json_schema: None,
@@ -324,7 +324,7 @@ async fn apply_patch_tool_executes_and_emits_patch_events() -> anyhow::Result<()
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "please apply a patch".into(),
}],
final_output_json_schema: None,
@@ -425,7 +425,7 @@ async fn apply_patch_reports_parse_diagnostics() -> anyhow::Result<()> {
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "please apply a patch".into(),
}],
final_output_json_schema: None,

View File

@@ -7,10 +7,10 @@ use std::time::Instant;
use codex_core::model_family::find_family_for_model;
use codex_core::protocol::AskForApproval;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_core::protocol::SandboxPolicy;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::user_input::UserInput;
use core_test_support::responses::ev_assistant_message;
use core_test_support::responses::ev_completed;
use core_test_support::responses::ev_function_call;
@@ -28,7 +28,7 @@ async fn run_turn(test: &TestCodex, prompt: &str) -> anyhow::Result<()> {
test.codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: prompt.into(),
}],
final_output_json_schema: None,

View File

@@ -6,10 +6,10 @@ use codex_core::features::Feature;
use codex_core::model_family::find_family_for_model;
use codex_core::protocol::AskForApproval;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_core::protocol::SandboxPolicy;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::user_input::UserInput;
use core_test_support::assert_regex_match;
use core_test_support::responses::ev_assistant_message;
use core_test_support::responses::ev_completed;
@@ -38,7 +38,7 @@ async fn submit_turn(
test.codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: prompt.into(),
}],
final_output_json_schema: None,

View File

@@ -6,10 +6,10 @@ use anyhow::Result;
use codex_core::features::Feature;
use codex_core::protocol::AskForApproval;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_core::protocol::SandboxPolicy;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::user_input::UserInput;
use core_test_support::responses::ev_assistant_message;
use core_test_support::responses::ev_completed;
use core_test_support::responses::ev_function_call;
@@ -118,7 +118,7 @@ async fn unified_exec_reuses_session_via_stdin() -> Result<()> {
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "run unified exec".into(),
}],
final_output_json_schema: None,
@@ -254,7 +254,7 @@ PY
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "exercise lag handling".into(),
}],
final_output_json_schema: None,
@@ -360,7 +360,7 @@ async fn unified_exec_timeout_and_followup_poll() -> Result<()> {
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "check timeout".into(),
}],
final_output_json_schema: None,

View File

@@ -3,8 +3,8 @@
use std::os::unix::fs::PermissionsExt;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_protocol::user_input::UserInput;
use core_test_support::fs_wait;
use core_test_support::responses;
use core_test_support::skip_if_no_network;
@@ -52,7 +52,7 @@ echo -n "${@: -1}" > $(dirname "${0}")/notify.txt"#,
// 1) Normal user input should hit server once.
codex
.submit(Op::UserInput {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "hello world".into(),
}],
})

View File

@@ -4,10 +4,10 @@ use base64::Engine;
use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
use codex_core::protocol::AskForApproval;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_core::protocol::SandboxPolicy;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::user_input::UserInput;
use core_test_support::responses;
use core_test_support::responses::ev_assistant_message;
use core_test_support::responses::ev_completed;
@@ -90,7 +90,7 @@ async fn view_image_tool_attaches_local_image() -> anyhow::Result<()> {
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "please add the screenshot".into(),
}],
final_output_json_schema: None,
@@ -189,7 +189,7 @@ async fn view_image_tool_errors_when_path_is_directory() -> anyhow::Result<()> {
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "please attach the folder".into(),
}],
final_output_json_schema: None,
@@ -254,7 +254,7 @@ async fn view_image_tool_errors_when_file_missing() -> anyhow::Result<()> {
codex
.submit(Op::UserTurn {
items: vec![InputItem::Text {
items: vec![UserInput::Text {
text: "please attach the missing image".into(),
}],
final_output_json_schema: None,