Add text element metadata to types (#9235)
Initial type tweaking PR to make the diff of https://github.com/openai/codex/pull/9116 smaller This should not change any behavior, just adds some fields to types
This commit is contained in:
parent
2a68b74b9b
commit
4a9c2bcc5a
62 changed files with 483 additions and 41 deletions
|
|
@ -197,6 +197,8 @@ impl ThreadHistoryBuilder {
|
|||
if !payload.message.trim().is_empty() {
|
||||
content.push(UserInput::Text {
|
||||
text: payload.message.clone(),
|
||||
// TODO: Thread text element ranges into thread history. Empty keeps old behavior.
|
||||
text_elements: Vec::new(),
|
||||
});
|
||||
}
|
||||
if let Some(images) = &payload.images {
|
||||
|
|
@ -244,6 +246,8 @@ mod tests {
|
|||
EventMsg::UserMessage(UserMessageEvent {
|
||||
message: "First turn".into(),
|
||||
images: Some(vec!["https://example.com/one.png".into()]),
|
||||
text_elements: Vec::new(),
|
||||
local_images: Vec::new(),
|
||||
}),
|
||||
EventMsg::AgentMessage(AgentMessageEvent {
|
||||
message: "Hi there".into(),
|
||||
|
|
@ -257,6 +261,8 @@ mod tests {
|
|||
EventMsg::UserMessage(UserMessageEvent {
|
||||
message: "Second turn".into(),
|
||||
images: None,
|
||||
text_elements: Vec::new(),
|
||||
local_images: Vec::new(),
|
||||
}),
|
||||
EventMsg::AgentMessage(AgentMessageEvent {
|
||||
message: "Reply two".into(),
|
||||
|
|
@ -277,6 +283,7 @@ mod tests {
|
|||
content: vec![
|
||||
UserInput::Text {
|
||||
text: "First turn".into(),
|
||||
text_elements: Vec::new(),
|
||||
},
|
||||
UserInput::Image {
|
||||
url: "https://example.com/one.png".into(),
|
||||
|
|
@ -308,7 +315,8 @@ mod tests {
|
|||
ThreadItem::UserMessage {
|
||||
id: "item-4".into(),
|
||||
content: vec![UserInput::Text {
|
||||
text: "Second turn".into()
|
||||
text: "Second turn".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
}
|
||||
);
|
||||
|
|
@ -327,6 +335,8 @@ mod tests {
|
|||
EventMsg::UserMessage(UserMessageEvent {
|
||||
message: "Turn start".into(),
|
||||
images: None,
|
||||
text_elements: Vec::new(),
|
||||
local_images: Vec::new(),
|
||||
}),
|
||||
EventMsg::AgentReasoning(AgentReasoningEvent {
|
||||
text: "first summary".into(),
|
||||
|
|
@ -371,6 +381,8 @@ mod tests {
|
|||
EventMsg::UserMessage(UserMessageEvent {
|
||||
message: "Please do the thing".into(),
|
||||
images: None,
|
||||
text_elements: Vec::new(),
|
||||
local_images: Vec::new(),
|
||||
}),
|
||||
EventMsg::AgentMessage(AgentMessageEvent {
|
||||
message: "Working...".into(),
|
||||
|
|
@ -381,6 +393,8 @@ mod tests {
|
|||
EventMsg::UserMessage(UserMessageEvent {
|
||||
message: "Let's try again".into(),
|
||||
images: None,
|
||||
text_elements: Vec::new(),
|
||||
local_images: Vec::new(),
|
||||
}),
|
||||
EventMsg::AgentMessage(AgentMessageEvent {
|
||||
message: "Second attempt complete.".into(),
|
||||
|
|
@ -398,7 +412,8 @@ mod tests {
|
|||
ThreadItem::UserMessage {
|
||||
id: "item-1".into(),
|
||||
content: vec![UserInput::Text {
|
||||
text: "Please do the thing".into()
|
||||
text: "Please do the thing".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
}
|
||||
);
|
||||
|
|
@ -418,7 +433,8 @@ mod tests {
|
|||
ThreadItem::UserMessage {
|
||||
id: "item-3".into(),
|
||||
content: vec![UserInput::Text {
|
||||
text: "Let's try again".into()
|
||||
text: "Let's try again".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
}
|
||||
);
|
||||
|
|
@ -437,6 +453,8 @@ mod tests {
|
|||
EventMsg::UserMessage(UserMessageEvent {
|
||||
message: "First".into(),
|
||||
images: None,
|
||||
text_elements: Vec::new(),
|
||||
local_images: Vec::new(),
|
||||
}),
|
||||
EventMsg::AgentMessage(AgentMessageEvent {
|
||||
message: "A1".into(),
|
||||
|
|
@ -444,6 +462,8 @@ mod tests {
|
|||
EventMsg::UserMessage(UserMessageEvent {
|
||||
message: "Second".into(),
|
||||
images: None,
|
||||
text_elements: Vec::new(),
|
||||
local_images: Vec::new(),
|
||||
}),
|
||||
EventMsg::AgentMessage(AgentMessageEvent {
|
||||
message: "A2".into(),
|
||||
|
|
@ -452,6 +472,8 @@ mod tests {
|
|||
EventMsg::UserMessage(UserMessageEvent {
|
||||
message: "Third".into(),
|
||||
images: None,
|
||||
text_elements: Vec::new(),
|
||||
local_images: Vec::new(),
|
||||
}),
|
||||
EventMsg::AgentMessage(AgentMessageEvent {
|
||||
message: "A3".into(),
|
||||
|
|
@ -469,6 +491,7 @@ mod tests {
|
|||
id: "item-1".into(),
|
||||
content: vec![UserInput::Text {
|
||||
text: "First".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
},
|
||||
ThreadItem::AgentMessage {
|
||||
|
|
@ -486,6 +509,7 @@ mod tests {
|
|||
id: "item-3".into(),
|
||||
content: vec![UserInput::Text {
|
||||
text: "Third".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
},
|
||||
ThreadItem::AgentMessage {
|
||||
|
|
@ -504,6 +528,8 @@ mod tests {
|
|||
EventMsg::UserMessage(UserMessageEvent {
|
||||
message: "One".into(),
|
||||
images: None,
|
||||
text_elements: Vec::new(),
|
||||
local_images: Vec::new(),
|
||||
}),
|
||||
EventMsg::AgentMessage(AgentMessageEvent {
|
||||
message: "A1".into(),
|
||||
|
|
@ -511,6 +537,8 @@ mod tests {
|
|||
EventMsg::UserMessage(UserMessageEvent {
|
||||
message: "Two".into(),
|
||||
images: None,
|
||||
text_elements: Vec::new(),
|
||||
local_images: Vec::new(),
|
||||
}),
|
||||
EventMsg::AgentMessage(AgentMessageEvent {
|
||||
message: "A2".into(),
|
||||
|
|
|
|||
|
|
@ -1543,21 +1543,55 @@ pub struct TurnInterruptParams {
|
|||
pub struct TurnInterruptResponse {}
|
||||
|
||||
// User input types
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct ByteRange {
|
||||
pub start: usize,
|
||||
pub end: usize,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct TextElement {
|
||||
/// Byte range in the parent `text` buffer that this element occupies.
|
||||
pub byte_range: ByteRange,
|
||||
/// Optional human-readable placeholder for the element, displayed in the UI.
|
||||
pub placeholder: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
#[ts(tag = "type")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub enum UserInput {
|
||||
Text { text: String },
|
||||
Image { url: String },
|
||||
LocalImage { path: PathBuf },
|
||||
Skill { name: String, path: PathBuf },
|
||||
Text {
|
||||
text: String,
|
||||
/// UI-defined spans within `text` used to render or persist special elements.
|
||||
#[serde(default)]
|
||||
text_elements: Vec<TextElement>,
|
||||
},
|
||||
Image {
|
||||
url: String,
|
||||
},
|
||||
LocalImage {
|
||||
path: PathBuf,
|
||||
},
|
||||
Skill {
|
||||
name: String,
|
||||
path: PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
impl UserInput {
|
||||
pub fn into_core(self) -> CoreUserInput {
|
||||
match self {
|
||||
UserInput::Text { text } => CoreUserInput::Text { text },
|
||||
UserInput::Text { text, .. } => CoreUserInput::Text {
|
||||
text,
|
||||
// TODO: Thread text element ranges into v2 inputs. Empty keeps old behavior.
|
||||
text_elements: Vec::new(),
|
||||
},
|
||||
UserInput::Image { url } => CoreUserInput::Image { image_url: url },
|
||||
UserInput::LocalImage { path } => CoreUserInput::LocalImage { path },
|
||||
UserInput::Skill { name, path } => CoreUserInput::Skill { name, path },
|
||||
|
|
@ -1568,7 +1602,11 @@ impl UserInput {
|
|||
impl From<CoreUserInput> for UserInput {
|
||||
fn from(value: CoreUserInput) -> Self {
|
||||
match value {
|
||||
CoreUserInput::Text { text } => UserInput::Text { text },
|
||||
CoreUserInput::Text { text, .. } => UserInput::Text {
|
||||
text,
|
||||
// TODO: Thread text element ranges from core into v2 inputs.
|
||||
text_elements: Vec::new(),
|
||||
},
|
||||
CoreUserInput::Image { image_url } => UserInput::Image { url: image_url },
|
||||
CoreUserInput::LocalImage { path } => UserInput::LocalImage { path },
|
||||
CoreUserInput::Skill { name, path } => UserInput::Skill { name, path },
|
||||
|
|
@ -2160,6 +2198,7 @@ mod tests {
|
|||
content: vec![
|
||||
CoreUserInput::Text {
|
||||
text: "hello".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
},
|
||||
CoreUserInput::Image {
|
||||
image_url: "https://example.com/image.png".to_string(),
|
||||
|
|
@ -2181,6 +2220,7 @@ mod tests {
|
|||
content: vec![
|
||||
UserInput::Text {
|
||||
text: "hello".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
},
|
||||
UserInput::Image {
|
||||
url: "https://example.com/image.png".to_string(),
|
||||
|
|
|
|||
|
|
@ -256,7 +256,11 @@ fn send_message_v2_with_policies(
|
|||
println!("< thread/start response: {thread_response:?}");
|
||||
let mut turn_params = TurnStartParams {
|
||||
thread_id: thread_response.thread.id.clone(),
|
||||
input: vec![V2UserInput::Text { text: user_message }],
|
||||
input: vec![V2UserInput::Text {
|
||||
text: user_message,
|
||||
// Plain text conversion has no UI element ranges.
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
turn_params.approval_policy = approval_policy;
|
||||
|
|
@ -288,6 +292,7 @@ fn send_follow_up_v2(
|
|||
thread_id: thread_response.thread.id.clone(),
|
||||
input: vec![V2UserInput::Text {
|
||||
text: first_message,
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
|
|
@ -299,6 +304,7 @@ fn send_follow_up_v2(
|
|||
thread_id: thread_response.thread.id.clone(),
|
||||
input: vec![V2UserInput::Text {
|
||||
text: follow_up_message,
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3125,7 +3125,11 @@ impl CodexMessageProcessor {
|
|||
let mapped_items: Vec<CoreInputItem> = items
|
||||
.into_iter()
|
||||
.map(|item| match item {
|
||||
WireInputItem::Text { text } => CoreInputItem::Text { text },
|
||||
WireInputItem::Text { text } => CoreInputItem::Text {
|
||||
text,
|
||||
// TODO: Thread text element ranges into v1 input handling.
|
||||
text_elements: Vec::new(),
|
||||
},
|
||||
WireInputItem::Image { image_url } => CoreInputItem::Image { image_url },
|
||||
WireInputItem::LocalImage { path } => CoreInputItem::LocalImage { path },
|
||||
})
|
||||
|
|
@ -3171,7 +3175,11 @@ impl CodexMessageProcessor {
|
|||
let mapped_items: Vec<CoreInputItem> = items
|
||||
.into_iter()
|
||||
.map(|item| match item {
|
||||
WireInputItem::Text { text } => CoreInputItem::Text { text },
|
||||
WireInputItem::Text { text } => CoreInputItem::Text {
|
||||
text,
|
||||
// TODO: Thread text element ranges into v1 input handling.
|
||||
text_elements: Vec::new(),
|
||||
},
|
||||
WireInputItem::Image { image_url } => CoreInputItem::Image { image_url },
|
||||
WireInputItem::LocalImage { path } => CoreInputItem::LocalImage { path },
|
||||
})
|
||||
|
|
@ -3333,6 +3341,7 @@ impl CodexMessageProcessor {
|
|||
id: turn_id.clone(),
|
||||
content: vec![V2UserInput::Text {
|
||||
text: display_text.to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
}]
|
||||
};
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ async fn turn_start_accepts_output_schema_v2() -> Result<()> {
|
|||
thread_id: thread.id.clone(),
|
||||
input: vec![V2UserInput::Text {
|
||||
text: "Hello".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
output_schema: Some(output_schema.clone()),
|
||||
..Default::default()
|
||||
|
|
@ -142,6 +143,7 @@ async fn turn_start_output_schema_is_per_turn_v2() -> Result<()> {
|
|||
thread_id: thread.id.clone(),
|
||||
input: vec![V2UserInput::Text {
|
||||
text: "Hello".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
output_schema: Some(output_schema.clone()),
|
||||
..Default::default()
|
||||
|
|
@ -183,6 +185,7 @@ async fn turn_start_output_schema_is_per_turn_v2() -> Result<()> {
|
|||
thread_id: thread.id.clone(),
|
||||
input: vec![V2UserInput::Text {
|
||||
text: "Hello again".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
output_schema: None,
|
||||
..Default::default()
|
||||
|
|
|
|||
|
|
@ -95,7 +95,8 @@ async fn thread_fork_creates_new_thread_and_emits_started() -> Result<()> {
|
|||
assert_eq!(
|
||||
content,
|
||||
&vec![UserInput::Text {
|
||||
text: preview.to_string()
|
||||
text: preview.to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,7 +118,8 @@ async fn thread_resume_returns_rollout_history() -> Result<()> {
|
|||
assert_eq!(
|
||||
content,
|
||||
&vec![UserInput::Text {
|
||||
text: preview.to_string()
|
||||
text: preview.to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ async fn thread_rollback_drops_last_turns_and_persists_to_rollout() -> Result<()
|
|||
thread_id: thread.id.clone(),
|
||||
input: vec![V2UserInput::Text {
|
||||
text: first_text.to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
..Default::default()
|
||||
})
|
||||
|
|
@ -77,6 +78,7 @@ async fn thread_rollback_drops_last_turns_and_persists_to_rollout() -> Result<()
|
|||
thread_id: thread.id.clone(),
|
||||
input: vec![V2UserInput::Text {
|
||||
text: "Second".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
..Default::default()
|
||||
})
|
||||
|
|
@ -115,7 +117,8 @@ async fn thread_rollback_drops_last_turns_and_persists_to_rollout() -> Result<()
|
|||
assert_eq!(
|
||||
content,
|
||||
&vec![V2UserInput::Text {
|
||||
text: first_text.to_string()
|
||||
text: first_text.to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
|
@ -143,7 +146,8 @@ async fn thread_rollback_drops_last_turns_and_persists_to_rollout() -> Result<()
|
|||
assert_eq!(
|
||||
content,
|
||||
&vec![V2UserInput::Text {
|
||||
text: first_text.to_string()
|
||||
text: first_text.to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ async fn turn_interrupt_aborts_running_turn() -> Result<()> {
|
|||
thread_id: thread.id.clone(),
|
||||
input: vec![V2UserInput::Text {
|
||||
text: "run sleep".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
cwd: Some(working_directory.clone()),
|
||||
..Default::default()
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ async fn turn_start_sends_originator_header() -> Result<()> {
|
|||
thread_id: thread.id.clone(),
|
||||
input: vec![V2UserInput::Text {
|
||||
text: "Hello".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
..Default::default()
|
||||
})
|
||||
|
|
@ -149,6 +150,7 @@ async fn turn_start_emits_notifications_and_accepts_model_override() -> Result<(
|
|||
thread_id: thread.id.clone(),
|
||||
input: vec![V2UserInput::Text {
|
||||
text: "Hello".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
..Default::default()
|
||||
})
|
||||
|
|
@ -181,6 +183,7 @@ async fn turn_start_emits_notifications_and_accepts_model_override() -> Result<(
|
|||
thread_id: thread.id.clone(),
|
||||
input: vec![V2UserInput::Text {
|
||||
text: "Second".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
model: Some("mock-model-override".to_string()),
|
||||
..Default::default()
|
||||
|
|
@ -331,6 +334,7 @@ async fn turn_start_exec_approval_toggle_v2() -> Result<()> {
|
|||
thread_id: thread.id.clone(),
|
||||
input: vec![V2UserInput::Text {
|
||||
text: "run python".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
..Default::default()
|
||||
})
|
||||
|
|
@ -376,6 +380,7 @@ async fn turn_start_exec_approval_toggle_v2() -> Result<()> {
|
|||
thread_id: thread.id.clone(),
|
||||
input: vec![V2UserInput::Text {
|
||||
text: "run python again".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
approval_policy: Some(codex_app_server_protocol::AskForApproval::Never),
|
||||
sandbox_policy: Some(codex_app_server_protocol::SandboxPolicy::DangerFullAccess),
|
||||
|
|
@ -452,6 +457,7 @@ async fn turn_start_exec_approval_decline_v2() -> Result<()> {
|
|||
thread_id: thread.id.clone(),
|
||||
input: vec![V2UserInput::Text {
|
||||
text: "run python".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
cwd: Some(workspace.clone()),
|
||||
..Default::default()
|
||||
|
|
@ -600,6 +606,7 @@ async fn turn_start_updates_sandbox_and_cwd_between_turns_v2() -> Result<()> {
|
|||
thread_id: thread.id.clone(),
|
||||
input: vec![V2UserInput::Text {
|
||||
text: "first turn".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
cwd: Some(first_cwd.clone()),
|
||||
approval_policy: Some(codex_app_server_protocol::AskForApproval::Never),
|
||||
|
|
@ -633,6 +640,7 @@ async fn turn_start_updates_sandbox_and_cwd_between_turns_v2() -> Result<()> {
|
|||
thread_id: thread.id.clone(),
|
||||
input: vec![V2UserInput::Text {
|
||||
text: "second turn".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
cwd: Some(second_cwd.clone()),
|
||||
approval_policy: Some(codex_app_server_protocol::AskForApproval::Never),
|
||||
|
|
@ -733,6 +741,7 @@ async fn turn_start_file_change_approval_v2() -> Result<()> {
|
|||
thread_id: thread.id.clone(),
|
||||
input: vec![V2UserInput::Text {
|
||||
text: "apply patch".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
cwd: Some(workspace.clone()),
|
||||
..Default::default()
|
||||
|
|
@ -910,6 +919,7 @@ async fn turn_start_file_change_approval_accept_for_session_persists_v2() -> Res
|
|||
thread_id: thread.id.clone(),
|
||||
input: vec![V2UserInput::Text {
|
||||
text: "apply patch 1".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
cwd: Some(workspace.clone()),
|
||||
..Default::default()
|
||||
|
|
@ -986,6 +996,7 @@ async fn turn_start_file_change_approval_accept_for_session_persists_v2() -> Res
|
|||
thread_id: thread.id.clone(),
|
||||
input: vec![V2UserInput::Text {
|
||||
text: "apply patch 2".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
cwd: Some(workspace.clone()),
|
||||
..Default::default()
|
||||
|
|
@ -1083,6 +1094,7 @@ async fn turn_start_file_change_approval_decline_v2() -> Result<()> {
|
|||
thread_id: thread.id.clone(),
|
||||
input: vec![V2UserInput::Text {
|
||||
text: "apply patch".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
cwd: Some(workspace.clone()),
|
||||
..Default::default()
|
||||
|
|
@ -1230,6 +1242,7 @@ unified_exec = true
|
|||
thread_id: thread.id.clone(),
|
||||
input: vec![V2UserInput::Text {
|
||||
text: "run a command".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
..Default::default()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -56,7 +56,11 @@ impl AgentControl {
|
|||
.send_op(
|
||||
agent_id,
|
||||
Op::UserInput {
|
||||
items: vec![UserInput::Text { text: prompt }],
|
||||
items: vec![UserInput::Text {
|
||||
text: prompt,
|
||||
// Plain text conversion has no UI element ranges.
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
},
|
||||
)
|
||||
|
|
@ -321,6 +325,7 @@ mod tests {
|
|||
Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello from tests".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
},
|
||||
|
|
@ -351,6 +356,7 @@ mod tests {
|
|||
Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "spawned".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2260,6 +2260,7 @@ mod handlers {
|
|||
Arc::clone(&turn_context),
|
||||
vec![UserInput::Text {
|
||||
text: turn_context.compact_prompt().to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
CompactTask,
|
||||
)
|
||||
|
|
@ -2470,6 +2471,7 @@ async fn spawn_review_thread(
|
|||
// Seed the child task with the review prompt as the initial user message.
|
||||
let input: Vec<UserInput> = vec![UserInput::Text {
|
||||
text: review_prompt,
|
||||
text_elements: Vec::new(),
|
||||
}];
|
||||
let tc = Arc::new(review_turn_context);
|
||||
sess.spawn_task(tc.clone(), input, ReviewTask::new()).await;
|
||||
|
|
@ -3964,6 +3966,7 @@ mod tests {
|
|||
let (sess, tc, rx) = make_session_and_context_with_rx().await;
|
||||
let input = vec![UserInput::Text {
|
||||
text: "hello".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}];
|
||||
sess.spawn_task(
|
||||
Arc::clone(&tc),
|
||||
|
|
@ -3993,6 +3996,7 @@ mod tests {
|
|||
let (sess, tc, rx) = make_session_and_context_with_rx().await;
|
||||
let input = vec![UserInput::Text {
|
||||
text: "hello".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}];
|
||||
sess.spawn_task(
|
||||
Arc::clone(&tc),
|
||||
|
|
@ -4019,6 +4023,7 @@ mod tests {
|
|||
let (sess, tc, rx) = make_session_and_context_with_rx().await;
|
||||
let input = vec![UserInput::Text {
|
||||
text: "start review".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}];
|
||||
sess.spawn_task(Arc::clone(&tc), input, ReviewTask::new())
|
||||
.await;
|
||||
|
|
|
|||
|
|
@ -44,7 +44,11 @@ pub(crate) async fn run_inline_auto_compact_task(
|
|||
turn_context: Arc<TurnContext>,
|
||||
) {
|
||||
let prompt = turn_context.compact_prompt().to_string();
|
||||
let input = vec![UserInput::Text { text: prompt }];
|
||||
let input = vec![UserInput::Text {
|
||||
text: prompt,
|
||||
// Plain text conversion has no UI element ranges.
|
||||
text_elements: Vec::new(),
|
||||
}];
|
||||
|
||||
run_compact_task_inner(sess, turn_context, input).await;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,11 @@ fn parse_user_message(message: &[ContentItem]) -> Option<UserMessageItem> {
|
|||
if is_session_prefix(text) || is_user_shell_command_text(text) {
|
||||
return None;
|
||||
}
|
||||
content.push(UserInput::Text { text: text.clone() });
|
||||
content.push(UserInput::Text {
|
||||
text: text.clone(),
|
||||
// Plain text conversion has no UI element ranges.
|
||||
text_elements: Vec::new(),
|
||||
});
|
||||
}
|
||||
ContentItem::InputImage { image_url } => {
|
||||
content.push(UserInput::Image {
|
||||
|
|
@ -179,6 +183,7 @@ mod tests {
|
|||
let expected_content = vec![
|
||||
UserInput::Text {
|
||||
text: "Hello world".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
},
|
||||
UserInput::Image { image_url: img1 },
|
||||
UserInput::Image { image_url: img2 },
|
||||
|
|
@ -218,7 +223,10 @@ mod tests {
|
|||
TurnItem::UserMessage(user) => {
|
||||
let expected_content = vec![
|
||||
UserInput::Image { image_url },
|
||||
UserInput::Text { text: user_text },
|
||||
UserInput::Text {
|
||||
text: user_text,
|
||||
text_elements: Vec::new(),
|
||||
},
|
||||
];
|
||||
assert_eq!(user.content, expected_content);
|
||||
}
|
||||
|
|
@ -255,7 +263,10 @@ mod tests {
|
|||
TurnItem::UserMessage(user) => {
|
||||
let expected_content = vec![
|
||||
UserInput::Image { image_url },
|
||||
UserInput::Text { text: user_text },
|
||||
UserInput::Text {
|
||||
text: user_text,
|
||||
text_elements: Vec::new(),
|
||||
},
|
||||
];
|
||||
assert_eq!(user.content, expected_content);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -603,6 +603,8 @@ async fn test_updated_at_uses_file_mtime() -> Result<()> {
|
|||
item: RolloutItem::EventMsg(EventMsg::UserMessage(UserMessageEvent {
|
||||
message: "hello".into(),
|
||||
images: None,
|
||||
text_elements: Vec::new(),
|
||||
local_images: Vec::new(),
|
||||
})),
|
||||
};
|
||||
writeln!(file, "{}", serde_json::to_string(&user_event_line)?)?;
|
||||
|
|
|
|||
|
|
@ -270,6 +270,7 @@ impl TestCodex {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: prompt.into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: self.cwd.path().to_path_buf(),
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ async fn interrupt_long_running_tool_emits_turn_aborted() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "start sleep".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -101,6 +102,7 @@ async fn interrupt_tool_records_history_entries() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "start history recording".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -118,6 +120,7 @@ async fn interrupt_tool_records_history_entries() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "follow up".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -302,6 +302,7 @@ async fn apply_patch_cli_move_without_content_change_has_no_turn_diff(
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "rename without content change".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -888,6 +889,7 @@ async fn apply_patch_shell_command_heredoc_with_cd_emits_turn_diff() -> Result<(
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "apply via shell heredoc with cd".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -965,6 +967,7 @@ async fn apply_patch_shell_command_failure_propagates_error_and_skips_diff() ->
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "apply patch via shell".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -1112,6 +1115,7 @@ async fn apply_patch_emits_turn_diff_event_with_unified_diff(
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "emit diff".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -1172,6 +1176,7 @@ async fn apply_patch_turn_diff_for_rename_with_content_change(
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "rename with change".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -1240,6 +1245,7 @@ async fn apply_patch_aggregates_diff_across_multiple_tool_calls() -> Result<()>
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "aggregate diffs".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -1308,6 +1314,7 @@ async fn apply_patch_aggregates_diff_preserves_success_after_failure() -> Result
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "apply patch twice with failure".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
|
|||
|
|
@ -492,6 +492,7 @@ async fn submit_turn(
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: prompt.into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: test.cwd.path().to_path_buf(),
|
||||
|
|
|
|||
|
|
@ -289,6 +289,7 @@ async fn resume_includes_initial_messages_and_sends_prior_items() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -387,6 +388,7 @@ async fn includes_conversation_id_and_model_headers_in_request() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -440,6 +442,7 @@ async fn includes_base_instructions_override_in_request() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -496,6 +499,7 @@ async fn chatgpt_auth_sends_correct_request() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -588,6 +592,7 @@ async fn prefers_apikey_when_config_prefers_apikey_even_with_chatgpt_tokens() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -629,6 +634,7 @@ async fn includes_user_instructions_message_in_request() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -708,6 +714,7 @@ async fn skills_append_to_instructions() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -760,6 +767,7 @@ async fn includes_configured_effort_in_request() -> anyhow::Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -797,6 +805,7 @@ async fn includes_no_effort_in_request() -> anyhow::Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -832,6 +841,7 @@ async fn includes_default_reasoning_effort_in_request_when_defined_by_model_info
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -871,6 +881,7 @@ async fn configured_reasoning_summary_is_sent() -> anyhow::Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -910,6 +921,7 @@ async fn reasoning_summary_is_omitted_when_disabled() -> anyhow::Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -943,6 +955,7 @@ async fn includes_default_verbosity_in_request() -> anyhow::Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -983,6 +996,7 @@ async fn configured_verbosity_not_sent_for_models_without_support() -> anyhow::R
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1022,6 +1036,7 @@ async fn configured_verbosity_is_sent() -> anyhow::Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1077,6 +1092,7 @@ async fn includes_developer_instructions_message_in_request() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1326,6 +1342,7 @@ async fn token_count_includes_rate_limits_snapshot() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1484,6 +1501,7 @@ async fn usage_limit_error_emits_rate_limit_event() -> anyhow::Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1554,6 +1572,7 @@ async fn context_window_error_sets_total_tokens_to_model_window() -> anyhow::Res
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "seed turn".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1565,6 +1584,7 @@ async fn context_window_error_sets_total_tokens_to_model_window() -> anyhow::Res
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "trigger context window".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1685,6 +1705,7 @@ async fn azure_overrides_assign_properties_used_for_responses_url() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1768,6 +1789,7 @@ async fn env_var_overrides_loaded_auth() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1840,7 +1862,10 @@ async fn history_dedupes_streamed_and_final_messages_across_turns() {
|
|||
// Turn 1: user sends U1; wait for completion.
|
||||
codex
|
||||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text { text: "U1".into() }],
|
||||
items: vec![UserInput::Text {
|
||||
text: "U1".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
.await
|
||||
|
|
@ -1850,7 +1875,10 @@ async fn history_dedupes_streamed_and_final_messages_across_turns() {
|
|||
// Turn 2: user sends U2; wait for completion.
|
||||
codex
|
||||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text { text: "U2".into() }],
|
||||
items: vec![UserInput::Text {
|
||||
text: "U2".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
.await
|
||||
|
|
@ -1860,7 +1888,10 @@ async fn history_dedupes_streamed_and_final_messages_across_turns() {
|
|||
// Turn 3: user sends U3; wait for completion.
|
||||
codex
|
||||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text { text: "U3".into() }],
|
||||
items: vec![UserInput::Text {
|
||||
text: "U3".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -159,6 +159,7 @@ async fn summarize_context_three_requests_and_instructions() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello world".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -180,6 +181,7 @@ async fn summarize_context_three_requests_and_instructions() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: THIRD_USER_MSG.into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -573,6 +575,7 @@ async fn multiple_auto_compact_per_task_runs_after_token_limit_hit() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: user_message.into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1049,6 +1052,7 @@ async fn auto_compact_runs_after_token_limit_hit() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: FIRST_AUTO_MSG.into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1061,6 +1065,7 @@ async fn auto_compact_runs_after_token_limit_hit() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: SECOND_AUTO_MSG.into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1073,6 +1078,7 @@ async fn auto_compact_runs_after_token_limit_hit() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: POST_AUTO_USER_MSG.into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1273,6 +1279,7 @@ async fn auto_compact_runs_after_resume_when_token_usage_is_over_limit() {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: follow_up_user.into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: resumed.cwd.path().to_path_buf(),
|
||||
|
|
@ -1382,6 +1389,7 @@ async fn auto_compact_persists_rollout_entries() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: FIRST_AUTO_MSG.into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1393,6 +1401,7 @@ async fn auto_compact_persists_rollout_entries() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: SECOND_AUTO_MSG.into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1404,6 +1413,7 @@ async fn auto_compact_persists_rollout_entries() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: POST_AUTO_USER_MSG.into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1496,6 +1506,7 @@ async fn manual_compact_retries_after_context_window_error() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "first turn".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1629,6 +1640,7 @@ async fn manual_compact_twice_preserves_latest_user_messages() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: first_user_message.into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1643,6 +1655,7 @@ async fn manual_compact_twice_preserves_latest_user_messages() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: second_user_message.into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1657,6 +1670,7 @@ async fn manual_compact_twice_preserves_latest_user_messages() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: final_user_message.into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1835,7 +1849,10 @@ async fn auto_compact_allows_multiple_attempts_when_interleaved_with_other_turn_
|
|||
for user in [MULTI_AUTO_MSG, follow_up_user, final_user] {
|
||||
codex
|
||||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text { text: user.into() }],
|
||||
items: vec![UserInput::Text {
|
||||
text: user.into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
.await
|
||||
|
|
@ -1948,6 +1965,7 @@ async fn auto_compact_triggers_after_function_call_over_95_percent_usage() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: FUNCTION_CALL_LIMIT_MSG.into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1960,6 +1978,7 @@ async fn auto_compact_triggers_after_function_call_over_95_percent_usage() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: follow_up_user.into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -2075,7 +2094,10 @@ async fn auto_compact_counts_encrypted_reasoning_before_last_user() {
|
|||
{
|
||||
codex
|
||||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text { text: user.into() }],
|
||||
items: vec![UserInput::Text {
|
||||
text: user.into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ async fn remote_compact_replaces_history_for_followups() -> Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello remote compact".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -86,6 +87,7 @@ async fn remote_compact_replaces_history_for_followups() -> Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "after compact".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -192,6 +194,7 @@ async fn remote_compact_runs_automatically() -> Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello remote compact".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -265,6 +268,7 @@ async fn remote_compact_persists_replacement_history_in_rollout() -> Result<()>
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "needs compaction".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -983,7 +983,10 @@ async fn start_test_conversation(
|
|||
async fn user_turn(conversation: &Arc<CodexThread>, text: &str) {
|
||||
conversation
|
||||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text { text: text.into() }],
|
||||
items: vec![UserInput::Text {
|
||||
text: text.into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ async fn execpolicy_blocks_shell_invocation() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "run shell command".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: test.cwd_path().to_path_buf(),
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ async fn fork_thread_twice_drops_to_first_message() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: text.to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ async fn copy_paste_local_image_persists_rollout_request_shape() -> anyhow::Resu
|
|||
},
|
||||
UserInput::Text {
|
||||
text: "pasted image".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
},
|
||||
],
|
||||
final_output_json_schema: None,
|
||||
|
|
@ -194,6 +195,7 @@ async fn drag_drop_image_persists_rollout_request_shape() -> anyhow::Result<()>
|
|||
},
|
||||
UserInput::Text {
|
||||
text: "dropped image".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
},
|
||||
],
|
||||
final_output_json_schema: None,
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ async fn user_message_item_is_emitted() -> anyhow::Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: (vec![UserInput::Text {
|
||||
text: "please inspect sample.txt".into(),
|
||||
text_elements: Vec::new(),
|
||||
}]),
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -69,12 +70,14 @@ async fn user_message_item_is_emitted() -> anyhow::Result<()> {
|
|||
started_item.content,
|
||||
vec![UserInput::Text {
|
||||
text: "please inspect sample.txt".into(),
|
||||
text_elements: Vec::new(),
|
||||
}]
|
||||
);
|
||||
assert_eq!(
|
||||
completed_item.content,
|
||||
vec![UserInput::Text {
|
||||
text: "please inspect sample.txt".into(),
|
||||
text_elements: Vec::new(),
|
||||
}]
|
||||
);
|
||||
Ok(())
|
||||
|
|
@ -99,6 +102,7 @@ async fn assistant_message_item_is_emitted() -> anyhow::Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "please summarize results".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -156,6 +160,7 @@ async fn reasoning_item_is_emitted() -> anyhow::Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "explain your reasoning".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -215,6 +220,7 @@ async fn web_search_item_is_emitted() -> anyhow::Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "find the weather".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -268,6 +274,7 @@ async fn agent_message_content_delta_has_item_metadata() -> anyhow::Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "please stream text".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -334,6 +341,7 @@ async fn reasoning_content_delta_has_item_metadata() -> anyhow::Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "reason through it".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -392,6 +400,7 @@ async fn reasoning_raw_content_delta_respects_flag() -> anyhow::Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "show raw reasoning".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ async fn codex_returns_json_result(model: String) -> anyhow::Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello world".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: Some(serde_json::from_str(SCHEMA)?),
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
|
|||
|
|
@ -87,7 +87,10 @@ async fn renews_cache_ttl_on_matching_models_etag() -> Result<()> {
|
|||
|
||||
codex
|
||||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text { text: "hi".into() }],
|
||||
items: vec![UserInput::Text {
|
||||
text: "hi".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: test.cwd_path().to_path_buf(),
|
||||
approval_policy: codex_core::protocol::AskForApproval::Never,
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ async fn refresh_models_on_models_etag_mismatch_and_avoid_duplicate_models_fetch
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "please run a tool".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ async fn responses_api_emits_api_request_event() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -87,6 +88,7 @@ async fn process_sse_emits_tracing_for_output_item() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -126,6 +128,7 @@ async fn process_sse_emits_failed_event_on_parse_error() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -166,6 +169,7 @@ async fn process_sse_records_failed_event_when_stream_closes_without_completed()
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -226,6 +230,7 @@ async fn process_sse_failed_event_records_response_error_message() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -284,6 +289,7 @@ async fn process_sse_failed_event_logs_parse_error() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -329,6 +335,7 @@ async fn process_sse_failed_event_logs_missing_error() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -383,6 +390,7 @@ async fn process_sse_failed_event_logs_response_completed_parse_error() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -434,6 +442,7 @@ async fn process_sse_emits_completed_telemetry() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -502,6 +511,7 @@ async fn handle_responses_span_records_response_kind_and_tool_name() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -567,6 +577,7 @@ async fn record_responses_sets_span_fields_for_response_events() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -647,6 +658,7 @@ async fn handle_response_item_records_tool_result_for_custom_tool_call() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -715,6 +727,7 @@ async fn handle_response_item_records_tool_result_for_function_call() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -793,6 +806,7 @@ async fn handle_response_item_records_tool_result_for_local_shell_missing_ids()
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -855,6 +869,7 @@ async fn handle_response_item_records_tool_result_for_local_shell_call() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -960,6 +975,7 @@ async fn handle_container_exec_autoapprove_from_config_records_tool_decision() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1009,6 +1025,7 @@ async fn handle_container_exec_user_approved_records_tool_decision() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "approved".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1068,6 +1085,7 @@ async fn handle_container_exec_user_approved_for_session_records_tool_decision()
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "persist".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1127,6 +1145,7 @@ async fn handle_sandbox_error_user_approves_retry_records_tool_decision() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "retry".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1186,6 +1205,7 @@ async fn handle_container_exec_user_denies_records_tool_decision() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "deny".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1245,6 +1265,7 @@ async fn handle_sandbox_error_user_approves_for_session_records_tool_decision()
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "persist".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -1305,6 +1326,7 @@ async fn handle_sandbox_error_user_denies_records_tool_decision() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "deny".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ async fn injected_user_input_triggers_follow_up_request_with_deltas() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "first prompt".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -115,6 +116,7 @@ async fn injected_user_input_triggers_follow_up_request_with_deltas() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "second prompt".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ async fn permissions_message_sent_once_on_start() -> Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -93,6 +94,7 @@ async fn permissions_message_added_on_override_change() -> Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 1".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -114,6 +116,7 @@ async fn permissions_message_added_on_override_change() -> Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 2".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -152,6 +155,7 @@ async fn permissions_message_not_added_when_no_change() -> Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 1".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -162,6 +166,7 @@ async fn permissions_message_not_added_when_no_change() -> Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 2".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -203,6 +208,7 @@ async fn resume_replays_permissions_messages() -> Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 1".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -226,6 +232,7 @@ async fn resume_replays_permissions_messages() -> Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 2".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -238,6 +245,7 @@ async fn resume_replays_permissions_messages() -> Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "after resume".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -276,6 +284,7 @@ async fn resume_and_fork_append_permissions_messages() -> Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 1".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -299,6 +308,7 @@ async fn resume_and_fork_append_permissions_messages() -> Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 2".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -319,6 +329,7 @@ async fn resume_and_fork_append_permissions_messages() -> Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "after resume".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -346,6 +357,7 @@ async fn resume_and_fork_append_permissions_messages() -> Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "after fork".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -393,6 +405,7 @@ async fn permissions_message_includes_writable_roots() -> Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ async fn prompt_tools_are_consistent_across_requests() -> anyhow::Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 1".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -118,6 +119,7 @@ async fn prompt_tools_are_consistent_across_requests() -> anyhow::Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 2".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -180,6 +182,7 @@ async fn codex_mini_latest_tools() -> anyhow::Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 1".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -190,6 +193,7 @@ async fn codex_mini_latest_tools() -> anyhow::Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 2".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -241,6 +245,7 @@ async fn prefixes_context_and_instructions_once_and_consistently_across_requests
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 1".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -251,6 +256,7 @@ async fn prefixes_context_and_instructions_once_and_consistently_across_requests
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 2".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -316,6 +322,7 @@ async fn overrides_turn_context_but_keeps_cached_prefix_and_key_constant() -> an
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 1".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -345,6 +352,7 @@ async fn overrides_turn_context_but_keeps_cached_prefix_and_key_constant() -> an
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 2".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -406,6 +414,7 @@ async fn override_before_first_turn_emits_environment_context() -> anyhow::Resul
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "first message".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -517,6 +526,7 @@ async fn per_turn_overrides_keep_cached_prefix_and_key_constant() -> anyhow::Res
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 1".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -536,6 +546,7 @@ async fn per_turn_overrides_keep_cached_prefix_and_key_constant() -> anyhow::Res
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 2".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
cwd: new_cwd.path().to_path_buf(),
|
||||
approval_policy: AskForApproval::Never,
|
||||
|
|
@ -627,6 +638,7 @@ async fn send_user_turn_with_no_changes_does_not_send_environment_context() -> a
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 1".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
cwd: default_cwd.clone(),
|
||||
approval_policy: default_approval_policy,
|
||||
|
|
@ -643,6 +655,7 @@ async fn send_user_turn_with_no_changes_does_not_send_environment_context() -> a
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 2".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
cwd: default_cwd.clone(),
|
||||
approval_policy: default_approval_policy,
|
||||
|
|
@ -720,6 +733,7 @@ async fn send_user_turn_with_changes_sends_environment_context() -> anyhow::Resu
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 1".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
cwd: default_cwd.clone(),
|
||||
approval_policy: default_approval_policy,
|
||||
|
|
@ -736,6 +750,7 @@ async fn send_user_turn_with_changes_sends_environment_context() -> anyhow::Resu
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello 2".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
cwd: default_cwd.clone(),
|
||||
approval_policy: AskForApproval::Never,
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ async fn quota_exceeded_emits_single_error_event() -> Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "quota?".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -165,6 +165,7 @@ async fn remote_models_remote_model_uses_unified_exec() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "run call".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -369,6 +370,7 @@ async fn remote_models_apply_remote_base_instructions() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello remote".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ async fn request_body_is_zstd_compressed_for_codex_backend_when_enabled() -> any
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "compress me".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -82,6 +83,7 @@ async fn request_body_is_not_compressed_for_api_key_auth_even_when_enabled() ->
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "do not compress".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ async fn resume_includes_initial_messages_from_rollout_events() -> Result<()> {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "Record some messages".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -89,6 +90,7 @@ async fn resume_includes_initial_messages_from_reasoning_events() -> Result<()>
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "Record reasoning messages".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -707,6 +707,7 @@ async fn review_history_surfaces_in_parent_session() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: followup.clone(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ async fn stdio_server_round_trip() -> anyhow::Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "call the rmcp echo tool".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: fixture.cwd.path().to_path_buf(),
|
||||
|
|
@ -244,6 +245,7 @@ async fn stdio_image_responses_round_trip() -> anyhow::Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "call the rmcp image tool".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: fixture.cwd.path().to_path_buf(),
|
||||
|
|
@ -438,6 +440,7 @@ async fn stdio_image_completions_round_trip() -> anyhow::Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "call the rmcp image tool".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: fixture.cwd.path().to_path_buf(),
|
||||
|
|
@ -580,6 +583,7 @@ async fn stdio_server_propagates_whitelisted_env_vars() -> anyhow::Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "call the rmcp echo tool".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: fixture.cwd.path().to_path_buf(),
|
||||
|
|
@ -733,6 +737,7 @@ async fn streamable_http_tool_call_round_trip() -> anyhow::Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "call the rmcp streamable http echo tool".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: fixture.cwd.path().to_path_buf(),
|
||||
|
|
@ -918,6 +923,7 @@ async fn streamable_http_with_oauth_round_trip() -> anyhow::Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "call the rmcp streamable http oauth echo tool".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: fixture.cwd.path().to_path_buf(),
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ async fn run_snapshot_command(command: &str) -> Result<SnapshotRun> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "run unified exec with shell snapshot".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd,
|
||||
|
|
@ -147,6 +148,7 @@ async fn run_shell_command_snapshot(command: &str) -> Result<SnapshotRun> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "run shell_command with shell snapshot".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd,
|
||||
|
|
@ -284,6 +286,7 @@ async fn shell_command_snapshot_still_intercepts_apply_patch() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "apply patch via shell_command with snapshot".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.clone(),
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ async fn user_turn_includes_skill_instructions() -> Result<()> {
|
|||
items: vec![
|
||||
UserInput::Text {
|
||||
text: "please use $demo".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
},
|
||||
UserInput::Skill {
|
||||
name: "demo".to_string(),
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ async fn continue_after_stream_error() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "first message".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
@ -106,6 +107,7 @@ async fn continue_after_stream_error() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "follow up".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ async fn retries_on_early_close() {
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ async fn shell_tool_executes_command_and_streams_output() -> anyhow::Result<()>
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "please run the shell command".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -148,6 +149,7 @@ async fn update_plan_tool_emits_plan_update_event() -> anyhow::Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "please update the plan".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -224,6 +226,7 @@ async fn update_plan_tool_rejects_malformed_payload() -> anyhow::Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "please update the plan".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -312,6 +315,7 @@ async fn apply_patch_tool_executes_and_emits_patch_events() -> anyhow::Result<()
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "please apply a patch".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -408,6 +412,7 @@ async fn apply_patch_reports_parse_diagnostics() -> anyhow::Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "please apply a patch".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ async fn run_turn(test: &TestCodex, prompt: &str) -> anyhow::Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: prompt.into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: test.cwd.path().to_path_buf(),
|
||||
|
|
@ -353,6 +354,7 @@ async fn shell_tools_start_before_response_completed_when_stream_delayed() -> an
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "stream delayed completion".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: test.cwd.path().to_path_buf(),
|
||||
|
|
|
|||
|
|
@ -536,6 +536,7 @@ async fn mcp_image_output_preserves_image_and_no_text_summary() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "call the rmcp image tool".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: fixture.cwd.path().to_path_buf(),
|
||||
|
|
|
|||
|
|
@ -200,6 +200,7 @@ async fn unified_exec_intercepts_apply_patch_exec_command() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "apply patch via unified exec".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd,
|
||||
|
|
@ -326,6 +327,7 @@ async fn unified_exec_emits_exec_command_begin_event() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "emit begin event".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -401,6 +403,7 @@ async fn unified_exec_resolves_relative_workdir() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "run relative workdir test".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -479,6 +482,7 @@ async fn unified_exec_respects_workdir_override() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "run workdir test".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -569,6 +573,7 @@ async fn unified_exec_emits_exec_command_end_event() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "emit end event".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -641,6 +646,7 @@ async fn unified_exec_emits_output_delta_for_exec_command() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "emit delta".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -714,6 +720,7 @@ async fn unified_exec_full_lifecycle_with_background_end_event() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "exercise full unified exec lifecycle".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -840,6 +847,7 @@ async fn unified_exec_emits_terminal_interaction_for_write_stdin() -> Result<()>
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "stdin delta".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -973,6 +981,7 @@ async fn unified_exec_terminal_interaction_captures_delayed_output() -> Result<(
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "delayed terminal interaction output".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -1130,6 +1139,7 @@ async fn unified_exec_emits_one_begin_and_one_end_event() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "check poll event behavior".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -1225,6 +1235,7 @@ async fn exec_command_reports_chunk_and_exit_metadata() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "run metadata test".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -1340,6 +1351,7 @@ async fn unified_exec_defaults_to_pipe() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "check default pipe mode".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -1427,6 +1439,7 @@ async fn unified_exec_can_enable_tty() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "check tty enabled".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -1505,6 +1518,7 @@ async fn unified_exec_respects_early_exit_notifications() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "watch early exit timing".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -1633,6 +1647,7 @@ async fn write_stdin_returns_exit_metadata_and_clears_session() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "test write_stdin exit behavior".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -1798,6 +1813,7 @@ async fn unified_exec_emits_end_event_when_session_dies_via_stdin() -> Result<()
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "end on exit".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -1872,6 +1888,7 @@ async fn unified_exec_closes_long_running_session_at_turn_end() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "close unified exec processes on turn end".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -1990,6 +2007,7 @@ async fn unified_exec_reuses_session_via_stdin() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "run unified exec".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -2122,6 +2140,7 @@ PY
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "exercise lag handling".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -2233,6 +2252,7 @@ async fn unified_exec_timeout_and_followup_poll() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "check timeout".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -2326,6 +2346,7 @@ PY
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "summarize large output".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -2404,6 +2425,7 @@ async fn unified_exec_runs_under_sandbox() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "summarize large output".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -2506,6 +2528,7 @@ async fn unified_exec_python_prompt_under_seatbelt() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "start python under seatbelt".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -2598,6 +2621,7 @@ async fn unified_exec_runs_on_all_platforms() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "summarize large output".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -2728,6 +2752,7 @@ async fn unified_exec_prunes_exited_sessions_first() -> Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "fill session cache".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ echo -n "${@: -1}" > $(dirname "${0}")/notify.txt"#,
|
|||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello world".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -171,6 +171,7 @@ async fn view_image_tool_attaches_local_image() -> anyhow::Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "please add the screenshot".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -301,6 +302,7 @@ async fn view_image_tool_errors_when_path_is_directory() -> anyhow::Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "please attach the folder".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -373,6 +375,7 @@ async fn view_image_tool_placeholder_for_non_image_files() -> anyhow::Result<()>
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "please use the view_image tool to read the json file".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
@ -464,6 +467,7 @@ async fn view_image_tool_errors_when_file_missing() -> anyhow::Result<()> {
|
|||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "please attach the missing image".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
|
|
|
|||
|
|
@ -184,7 +184,11 @@ impl AppServerClient {
|
|||
request_id: request_id.clone(),
|
||||
params: TurnStartParams {
|
||||
thread_id: thread_id.to_string(),
|
||||
input: vec![UserInput::Text { text }],
|
||||
input: vec![UserInput::Text {
|
||||
text,
|
||||
// Plain text conversion has no UI element ranges.
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -360,6 +360,7 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>) -> any
|
|||
.collect();
|
||||
items.push(UserInput::Text {
|
||||
text: prompt_text.clone(),
|
||||
text_elements: Vec::new(),
|
||||
});
|
||||
let output_schema = load_output_schema(output_schema_path.clone());
|
||||
(
|
||||
|
|
@ -378,6 +379,7 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>) -> any
|
|||
.collect();
|
||||
items.push(UserInput::Text {
|
||||
text: prompt_text.clone(),
|
||||
text_elements: Vec::new(),
|
||||
});
|
||||
let output_schema = load_output_schema(output_schema_path);
|
||||
(
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ pub async fn run_codex_tool_session(
|
|||
op: Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: initial_prompt.clone(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
},
|
||||
|
|
@ -158,7 +159,11 @@ pub async fn run_codex_tool_session_reply(
|
|||
.insert(request_id.clone(), thread_id);
|
||||
if let Err(e) = thread
|
||||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text { text: prompt }],
|
||||
items: vec![UserInput::Text {
|
||||
text: prompt,
|
||||
// Plain text conversion has no UI element ranges.
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@ impl OtelManager {
|
|||
let prompt = items
|
||||
.iter()
|
||||
.flat_map(|item| match item {
|
||||
UserInput::Text { text } => Some(text.as_str()),
|
||||
UserInput::Text { text, .. } => Some(text.as_str()),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<String>();
|
||||
|
|
|
|||
|
|
@ -65,6 +65,10 @@ impl UserMessageItem {
|
|||
EventMsg::UserMessage(UserMessageEvent {
|
||||
message: self.message(),
|
||||
images: Some(self.image_urls()),
|
||||
// TODO: Thread text element ranges into legacy user message events.
|
||||
text_elements: Vec::new(),
|
||||
// TODO: Thread local image paths into legacy user message events.
|
||||
local_images: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +76,7 @@ impl UserMessageItem {
|
|||
self.content
|
||||
.iter()
|
||||
.map(|c| match c {
|
||||
UserInput::Text { text } => text.clone(),
|
||||
UserInput::Text { text, .. } => text.clone(),
|
||||
_ => String::new(),
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
|
|
|
|||
|
|
@ -550,7 +550,7 @@ impl From<Vec<UserInput>> for ResponseInputItem {
|
|||
content: items
|
||||
.into_iter()
|
||||
.flat_map(|c| match c {
|
||||
UserInput::Text { text } => vec![ContentItem::InputText { text }],
|
||||
UserInput::Text { text, .. } => vec![ContentItem::InputText { text }],
|
||||
UserInput::Image { image_url } => vec![
|
||||
ContentItem::InputText {
|
||||
text: image_open_tag_text(),
|
||||
|
|
|
|||
|
|
@ -1255,8 +1255,19 @@ pub struct AgentMessageEvent {
|
|||
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, TS)]
|
||||
pub struct UserMessageEvent {
|
||||
pub message: String,
|
||||
/// Image URLs sourced from `UserInput::Image`. These are safe
|
||||
/// to replay in legacy UI history events and correspond to images sent to
|
||||
/// the model.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub images: Option<Vec<String>>,
|
||||
/// Local file paths sourced from `UserInput::LocalImage`. These are kept so
|
||||
/// the UI can reattach images when editing history, and should not be sent
|
||||
/// to the model or treated as API-ready URLs.
|
||||
#[serde(default)]
|
||||
pub local_images: Vec<std::path::PathBuf>,
|
||||
/// UI-defined spans within `message` used to render or persist special elements.
|
||||
#[serde(default)]
|
||||
pub text_elements: Vec<crate::user_input::TextElement>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, TS)]
|
||||
|
|
@ -2298,6 +2309,48 @@ mod tests {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn user_input_text_serializes_empty_text_elements() -> Result<()> {
|
||||
let input = UserInput::Text {
|
||||
text: "hello".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
};
|
||||
|
||||
let json_input = serde_json::to_value(input)?;
|
||||
assert_eq!(
|
||||
json_input,
|
||||
json!({
|
||||
"type": "text",
|
||||
"text": "hello",
|
||||
"text_elements": [],
|
||||
})
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn user_message_event_serializes_empty_metadata_vectors() -> Result<()> {
|
||||
let event = UserMessageEvent {
|
||||
message: "hello".to_string(),
|
||||
images: None,
|
||||
local_images: Vec::new(),
|
||||
text_elements: Vec::new(),
|
||||
};
|
||||
|
||||
let json_event = serde_json::to_value(event)?;
|
||||
assert_eq!(
|
||||
json_event,
|
||||
json!({
|
||||
"message": "hello",
|
||||
"local_images": [],
|
||||
"text_elements": [],
|
||||
})
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Serialize Event to verify that its JSON representation has the expected
|
||||
/// amount of nesting.
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -10,17 +10,19 @@ use ts_rs::TS;
|
|||
pub enum UserInput {
|
||||
Text {
|
||||
text: String,
|
||||
/// UI-defined spans within `text` that should be treated as special elements.
|
||||
/// These are byte ranges into the UTF-8 `text` buffer and are used to render
|
||||
/// or persist rich input markers (e.g., image placeholders) across history
|
||||
/// and resume without mutating the literal text.
|
||||
#[serde(default)]
|
||||
text_elements: Vec<TextElement>,
|
||||
},
|
||||
/// Pre‑encoded data: URI image.
|
||||
Image {
|
||||
image_url: String,
|
||||
},
|
||||
Image { image_url: String },
|
||||
|
||||
/// Local image path provided by the user. This will be converted to an
|
||||
/// `Image` variant (base64 data URL) during request serialization.
|
||||
LocalImage {
|
||||
path: std::path::PathBuf,
|
||||
},
|
||||
LocalImage { path: std::path::PathBuf },
|
||||
|
||||
/// Skill selected by the user (name + path to SKILL.md).
|
||||
Skill {
|
||||
|
|
@ -28,3 +30,28 @@ pub enum UserInput {
|
|||
path: std::path::PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, TS, JsonSchema)]
|
||||
pub struct TextElement {
|
||||
/// Byte range in the parent `text` buffer that this element occupies.
|
||||
pub byte_range: ByteRange,
|
||||
/// Optional human-readable placeholder for the element, displayed in the UI.
|
||||
pub placeholder: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, TS, JsonSchema)]
|
||||
pub struct ByteRange {
|
||||
/// Start byte offset (inclusive) within the UTF-8 text buffer.
|
||||
pub start: usize,
|
||||
/// End byte offset (exclusive) within the UTF-8 text buffer.
|
||||
pub end: usize,
|
||||
}
|
||||
|
||||
impl From<std::ops::Range<usize>> for ByteRange {
|
||||
fn from(range: std::ops::Range<usize>) -> Self {
|
||||
Self {
|
||||
start: range.start,
|
||||
end: range.end,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2266,7 +2266,11 @@ impl ChatWidget {
|
|||
}
|
||||
|
||||
if !text.is_empty() {
|
||||
items.push(UserInput::Text { text: text.clone() });
|
||||
// TODO: Thread text element ranges from the composer input. Empty keeps old behavior.
|
||||
items.push(UserInput::Text {
|
||||
text: text.clone(),
|
||||
text_elements: Vec::new(),
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(skills) = self.bottom_pane.skills() {
|
||||
|
|
|
|||
|
|
@ -143,6 +143,8 @@ async fn resumed_initial_messages_render_history() {
|
|||
EventMsg::UserMessage(UserMessageEvent {
|
||||
message: "hello from user".to_string(),
|
||||
images: None,
|
||||
text_elements: Vec::new(),
|
||||
local_images: Vec::new(),
|
||||
}),
|
||||
EventMsg::AgentMessage(AgentMessageEvent {
|
||||
message: "assistant reply".to_string(),
|
||||
|
|
|
|||
|
|
@ -2017,7 +2017,11 @@ impl ChatWidget {
|
|||
}
|
||||
|
||||
if !text.is_empty() {
|
||||
items.push(UserInput::Text { text: text.clone() });
|
||||
// TODO: Thread text element ranges from the composer input. Empty keeps old behavior.
|
||||
items.push(UserInput::Text {
|
||||
text: text.clone(),
|
||||
text_elements: Vec::new(),
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(skills) = self.bottom_pane.skills() {
|
||||
|
|
|
|||
|
|
@ -132,6 +132,8 @@ async fn resumed_initial_messages_render_history() {
|
|||
EventMsg::UserMessage(UserMessageEvent {
|
||||
message: "hello from user".to_string(),
|
||||
images: None,
|
||||
text_elements: Vec::new(),
|
||||
local_images: Vec::new(),
|
||||
}),
|
||||
EventMsg::AgentMessage(AgentMessageEvent {
|
||||
message: "assistant reply".to_string(),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue