mirror of
https://github.com/openai/codex.git
synced 2026-10-29 19:08:43 +00:00
response wiring
This commit is contained in:
@@ -132,6 +132,7 @@ async fn auto_compaction_remote_emits_started_and_completed_items() -> Result<()
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "REMOTE_COMPACT_SUMMARY".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
|
||||
@@ -757,6 +757,7 @@ async fn thread_resume_rejects_history_when_thread_is_running() -> Result<()> {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "history override".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}]),
|
||||
@@ -1494,6 +1495,7 @@ async fn thread_resume_supports_history_and_overrides() -> Result<()> {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: history_text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}];
|
||||
|
||||
@@ -297,6 +297,7 @@ async fn azure_default_store_attaches_ids_and_headers() -> Result<()> {
|
||||
id: Some("msg_1".into()),
|
||||
role: "user".into(),
|
||||
content: vec![ContentItem::InputText { text: "hi".into() }],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}],
|
||||
|
||||
@@ -489,6 +489,7 @@ mod tests {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "first request".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}],
|
||||
@@ -514,6 +515,7 @@ mod tests {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "commentary".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: Some(MessagePhase::Commentary),
|
||||
}],
|
||||
@@ -528,6 +530,7 @@ mod tests {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "final response".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: Some(MessagePhase::FinalAnswer),
|
||||
}],
|
||||
@@ -542,6 +545,7 @@ mod tests {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "latest request".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}],
|
||||
@@ -695,6 +699,7 @@ mod tests {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "please run the tool".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}],
|
||||
@@ -774,6 +779,7 @@ mod tests {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "please run the tool".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}],
|
||||
@@ -843,6 +849,7 @@ mod tests {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "please run the tool".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}],
|
||||
|
||||
@@ -3240,6 +3240,7 @@ impl Session {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: format!("Warning: {}", message.into()),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -3659,6 +3660,38 @@ impl Session {
|
||||
response_item: ResponseItem,
|
||||
user_message_type: Option<UserMessageType>,
|
||||
) {
|
||||
let user_message_type = if self.enabled(Feature::UserMessageTypeMetadata) {
|
||||
user_message_type
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let response_item = match (response_item, user_message_type.clone()) {
|
||||
(
|
||||
ResponseItem::Message {
|
||||
id,
|
||||
role,
|
||||
content,
|
||||
metadata,
|
||||
end_turn,
|
||||
phase,
|
||||
},
|
||||
Some(kind),
|
||||
) if role == "user" => {
|
||||
let mut metadata = metadata.unwrap_or_default();
|
||||
metadata.user_message_type = Some(kind);
|
||||
ResponseItem::Message {
|
||||
id,
|
||||
role,
|
||||
content,
|
||||
metadata: Some(metadata),
|
||||
end_turn,
|
||||
phase,
|
||||
}
|
||||
}
|
||||
(response_item, _) => response_item,
|
||||
};
|
||||
|
||||
// Persist the user message to history, but emit the turn item from `UserInput` so
|
||||
// UI-only `text_elements` are preserved. `ResponseItem::Message` does not carry
|
||||
// those spans, and `record_response_item_and_emit_turn_item` would drop them.
|
||||
|
||||
@@ -16,6 +16,7 @@ fn user_message(text: &str) -> ResponseItem {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -28,6 +29,7 @@ fn assistant_message(text: &str) -> ResponseItem {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
|
||||
@@ -107,6 +107,7 @@ fn user_message(text: &str) -> ResponseItem {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -119,6 +120,7 @@ fn assistant_message(text: &str) -> ResponseItem {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -131,6 +133,7 @@ fn skill_message(text: &str) -> ResponseItem {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -764,6 +767,7 @@ async fn reconstruct_history_uses_replacement_history_verbatim() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "summary".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -775,6 +779,7 @@ async fn reconstruct_history_uses_replacement_history_verbatim() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "stale developer instructions".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -3340,6 +3345,7 @@ async fn record_context_updates_and_set_reference_context_item_reinjects_full_co
|
||||
content: vec![ContentItem::InputText {
|
||||
text: format!("{}\nsummary", crate::compact::SUMMARY_PREFIX),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -3716,6 +3722,7 @@ async fn task_finish_emits_turn_item_lifecycle_for_leftover_pending_user_input()
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "late pending input".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -3737,19 +3744,12 @@ async fn task_finish_emits_turn_item_lifecycle_for_leftover_pending_user_input()
|
||||
assert!(matches!(
|
||||
second.msg,
|
||||
EventMsg::ItemStarted(ItemStartedEvent {
|
||||
item: TurnItem::UserMessage(UserMessageItem {
|
||||
content,
|
||||
metadata,
|
||||
..
|
||||
}),
|
||||
item: TurnItem::UserMessage(UserMessageItem { content, .. }),
|
||||
..
|
||||
}) if content == vec![UserInput::Text {
|
||||
text: "late pending input".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}] && metadata
|
||||
.as_ref()
|
||||
.and_then(|item_metadata| item_metadata.user_message_type.as_ref())
|
||||
== Some(&codex_protocol::items::UserMessageType::PromptQueued)
|
||||
}]
|
||||
));
|
||||
|
||||
let third = tokio::time::timeout(std::time::Duration::from_secs(2), rx.recv())
|
||||
@@ -3759,19 +3759,12 @@ async fn task_finish_emits_turn_item_lifecycle_for_leftover_pending_user_input()
|
||||
assert!(matches!(
|
||||
third.msg,
|
||||
EventMsg::ItemCompleted(ItemCompletedEvent {
|
||||
item: TurnItem::UserMessage(UserMessageItem {
|
||||
content,
|
||||
metadata,
|
||||
..
|
||||
}),
|
||||
item: TurnItem::UserMessage(UserMessageItem { content, .. }),
|
||||
..
|
||||
}) if content == vec![UserInput::Text {
|
||||
text: "late pending input".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}] && metadata
|
||||
.as_ref()
|
||||
.and_then(|item_metadata| item_metadata.user_message_type.as_ref())
|
||||
== Some(&codex_protocol::items::UserMessageType::PromptQueued)
|
||||
}]
|
||||
));
|
||||
|
||||
let fourth = tokio::time::timeout(std::time::Duration::from_secs(2), rx.recv())
|
||||
@@ -4077,6 +4070,7 @@ async fn sample_rollout(
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "first user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -4092,6 +4086,7 @@ async fn sample_rollout(
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "assistant reply one".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -4119,6 +4114,7 @@ async fn sample_rollout(
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "second user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -4134,6 +4130,7 @@ async fn sample_rollout(
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "assistant reply two".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -4161,6 +4158,7 @@ async fn sample_rollout(
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "third user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -4176,6 +4174,7 @@ async fn sample_rollout(
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "assistant reply three".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
|
||||
@@ -367,6 +367,7 @@ fn build_compacted_history_with_limit(
|
||||
content: vec![ContentItem::InputText {
|
||||
text: message.clone(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
});
|
||||
@@ -382,6 +383,7 @@ fn build_compacted_history_with_limit(
|
||||
id: None,
|
||||
role: "user".to_string(),
|
||||
content: vec![ContentItem::InputText { text: summary_text }],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
});
|
||||
@@ -501,6 +503,7 @@ mod tests {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "ignored".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -510,6 +513,7 @@ mod tests {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "first".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -535,6 +539,7 @@ do things
|
||||
</INSTRUCTIONS>"#
|
||||
.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -544,6 +549,7 @@ do things
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "<ENVIRONMENT_CONTEXT>cwd=/tmp</ENVIRONMENT_CONTEXT>".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -553,6 +559,7 @@ do things
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "real user message".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -636,6 +643,7 @@ do things
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "stale permissions".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -645,6 +653,7 @@ do things
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "summary".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -654,6 +663,7 @@ do things
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "stale personality".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -666,6 +676,7 @@ do things
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "summary".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
});
|
||||
@@ -680,6 +691,7 @@ do things
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "summary".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}];
|
||||
@@ -691,6 +703,7 @@ do things
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "summary".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
});
|
||||
@@ -711,6 +724,7 @@ keep me updated
|
||||
</INSTRUCTIONS>"#
|
||||
.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -724,6 +738,7 @@ keep me updated
|
||||
</environment_context>"#
|
||||
.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -737,6 +752,7 @@ keep me updated
|
||||
</turn_aborted>"#
|
||||
.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -746,6 +762,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "summary".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -755,6 +772,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "stale developer instructions".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -767,6 +785,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "summary".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
});
|
||||
@@ -782,6 +801,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "older user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -791,6 +811,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: format!("{SUMMARY_PREFIX}\nsummary text"),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -800,6 +821,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "latest user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -814,6 +836,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "older user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -823,6 +846,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: format!("{SUMMARY_PREFIX}\nsummary text"),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -834,6 +858,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "latest user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
});
|
||||
@@ -848,6 +873,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "summary".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}];
|
||||
@@ -878,6 +904,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "summary".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
});
|
||||
@@ -893,6 +920,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "older user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -902,6 +930,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "latest user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -911,6 +940,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: format!("{SUMMARY_PREFIX}\nsummary text"),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -921,6 +951,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "fresh permissions".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}];
|
||||
@@ -936,6 +967,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "older user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -945,6 +977,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "fresh permissions".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -954,6 +987,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "latest user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -963,6 +997,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: format!("{SUMMARY_PREFIX}\nsummary text"),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -981,6 +1016,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "fresh permissions".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}];
|
||||
@@ -996,6 +1032,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "fresh permissions".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
|
||||
@@ -33,6 +33,7 @@ fn assistant_msg(text: &str) -> ResponseItem {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -53,6 +54,7 @@ fn user_msg(text: &str) -> ResponseItem {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -65,6 +67,7 @@ fn user_input_text_msg(text: &str) -> ResponseItem {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -120,6 +123,7 @@ fn filters_non_api_messages() {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "ignored".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -151,6 +155,7 @@ fn filters_non_api_messages() {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "hi".to_string()
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -160,6 +165,7 @@ fn filters_non_api_messages() {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "hello".to_string()
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -265,6 +271,7 @@ fn for_prompt_strips_images_when_model_does_not_support_images() {
|
||||
text: "caption".to_string(),
|
||||
},
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -326,6 +333,7 @@ fn for_prompt_strips_images_when_model_does_not_support_images() {
|
||||
text: "caption".to_string(),
|
||||
},
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -382,6 +390,7 @@ fn for_prompt_strips_images_when_model_does_not_support_images() {
|
||||
image_url: "https://example.com/img.png".to_string(),
|
||||
},
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}]);
|
||||
@@ -410,6 +419,7 @@ fn for_prompt_rewrites_image_generation_calls_when_images_are_supported() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "hi".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -435,6 +445,7 @@ fn for_prompt_rewrites_image_generation_calls_when_images_are_supported() {
|
||||
image_url: "data:image/png;base64,Zm9v".to_string(),
|
||||
},
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -444,6 +455,7 @@ fn for_prompt_rewrites_image_generation_calls_when_images_are_supported() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "hi".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -460,6 +472,7 @@ fn for_prompt_rewrites_image_generation_calls_when_images_are_unsupported() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "generate a lobster".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -480,6 +493,7 @@ fn for_prompt_rewrites_image_generation_calls_when_images_are_unsupported() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "generate a lobster".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -501,6 +515,7 @@ fn for_prompt_rewrites_image_generation_calls_when_images_are_unsupported() {
|
||||
.to_string(),
|
||||
},
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -648,6 +663,7 @@ fn replace_last_turn_images_does_not_touch_user_images() {
|
||||
content: vec![ContentItem::InputImage {
|
||||
image_url: "data:image/png;base64,AAA".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}];
|
||||
@@ -1409,6 +1425,7 @@ fn image_data_url_payload_does_not_dominate_message_estimate() {
|
||||
},
|
||||
ContentItem::InputImage { image_url },
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -1418,6 +1435,7 @@ fn image_data_url_payload_does_not_dominate_message_estimate() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "Here is the screenshot".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -1490,6 +1508,7 @@ fn non_base64_image_urls_are_unchanged() {
|
||||
content: vec![ContentItem::InputImage {
|
||||
image_url: "https://example.com/foo.png".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -1521,6 +1540,7 @@ fn data_url_without_base64_marker_is_unchanged() {
|
||||
content: vec![ContentItem::InputImage {
|
||||
image_url: "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'/>".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -1559,6 +1579,7 @@ fn mixed_case_data_url_markers_are_adjusted() {
|
||||
id: None,
|
||||
role: "user".to_string(),
|
||||
content: vec![ContentItem::InputImage { image_url }],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -1590,6 +1611,7 @@ fn multiple_inline_images_apply_multiple_fixed_costs() {
|
||||
image_url: image_url_two,
|
||||
},
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -1673,6 +1695,7 @@ fn text_only_items_unchanged() {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "Hello world, this is a response.".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
|
||||
@@ -243,6 +243,7 @@ pub(crate) fn rewrite_image_generation_calls_for_stateless_input(items: &mut Vec
|
||||
},
|
||||
ContentItem::InputImage { image_url },
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
|
||||
@@ -178,6 +178,7 @@ fn build_text_message(role: &str, text_sections: Vec<String>) -> Option<Response
|
||||
id: None,
|
||||
role: role.to_string(),
|
||||
content,
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
})
|
||||
|
||||
@@ -57,6 +57,7 @@ impl ContextualUserFragmentDefinition {
|
||||
id: None,
|
||||
role: "user".to_string(),
|
||||
content: vec![ContentItem::InputText { text }],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
|
||||
@@ -201,6 +201,7 @@ mod tests {
|
||||
image_url: img2.clone(),
|
||||
},
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -244,6 +245,7 @@ mod tests {
|
||||
text: user_text.clone(),
|
||||
},
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -286,6 +288,7 @@ mod tests {
|
||||
text: user_text.clone(),
|
||||
},
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -316,6 +319,7 @@ mod tests {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "# AGENTS.md instructions for test_directory\n\n<INSTRUCTIONS>\ntest_text\n</INSTRUCTIONS>".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -325,6 +329,7 @@ mod tests {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "<environment_context>test_text</environment_context>".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -334,6 +339,7 @@ mod tests {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "# AGENTS.md instructions for test_directory\n\n<INSTRUCTIONS>\ntest_text\n</INSTRUCTIONS>".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -344,6 +350,7 @@ mod tests {
|
||||
text: "<skill>\n<name>demo</name>\n<path>skills/demo/SKILL.md</path>\nbody\n</skill>"
|
||||
.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -353,6 +360,7 @@ mod tests {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "<user_shell_command>echo 42</user_shell_command>".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -369,6 +377,7 @@ mod tests {
|
||||
.to_string(),
|
||||
},
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -388,6 +397,7 @@ mod tests {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "Hello from Codex".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
|
||||
@@ -166,6 +166,8 @@ pub enum Feature {
|
||||
ToolCallMcpElicitation,
|
||||
/// Enable personality selection in the TUI.
|
||||
Personality,
|
||||
/// Emit user_message_type metadata on user message turn items and request items.
|
||||
UserMessageTypeMetadata,
|
||||
/// Enable native artifact tools.
|
||||
Artifact,
|
||||
/// Enable Fast mode selection in the TUI and request layer.
|
||||
@@ -784,6 +786,12 @@ pub const FEATURES: &[FeatureSpec] = &[
|
||||
stage: Stage::Stable,
|
||||
default_enabled: true,
|
||||
},
|
||||
FeatureSpec {
|
||||
id: Feature::UserMessageTypeMetadata,
|
||||
key: "user_message_type_metadata",
|
||||
stage: Stage::UnderDevelopment,
|
||||
default_enabled: false,
|
||||
},
|
||||
FeatureSpec {
|
||||
id: Feature::Artifact,
|
||||
key: "artifact",
|
||||
|
||||
@@ -64,6 +64,7 @@ fn collect_guardian_transcript_entries_skips_contextual_user_messages() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "<environment_context>\n<cwd>/tmp</cwd>\n</environment_context>".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -73,6 +74,7 @@ fn collect_guardian_transcript_entries_skips_contextual_user_messages() {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "hello".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -99,6 +101,7 @@ fn collect_guardian_transcript_entries_includes_recent_tool_calls_and_output() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "check the repo".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -120,6 +123,7 @@ fn collect_guardian_transcript_entries_includes_recent_tool_calls_and_output() {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "I need to push a fix".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -313,6 +317,7 @@ async fn guardian_review_request_layout_matches_model_visible_request_snapshot()
|
||||
text: "Please check the repo visibility and push the docs fix if needed."
|
||||
.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -335,6 +340,7 @@ async fn guardian_review_request_layout_matches_model_visible_request_snapshot()
|
||||
text: "The repo is public; I now need approval to push the docs fix."
|
||||
.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
|
||||
@@ -330,6 +330,7 @@ mod job {
|
||||
&rollout_contents,
|
||||
)?,
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}],
|
||||
|
||||
@@ -1134,6 +1134,7 @@ async fn test_updated_at_uses_file_mtime() -> Result<()> {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: format!("reply-{idx}"),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}),
|
||||
|
||||
@@ -85,6 +85,7 @@ mod tests {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -97,6 +98,7 @@ mod tests {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
|
||||
@@ -405,6 +405,7 @@ mod tests {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: Some(true),
|
||||
phase: None,
|
||||
}
|
||||
|
||||
@@ -433,6 +433,7 @@ impl Session {
|
||||
"{TURN_ABORTED_OPEN_TAG}\n{TURN_ABORTED_INTERRUPTED_GUIDANCE}\n</turn_aborted>"
|
||||
),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
|
||||
@@ -239,6 +239,7 @@ pub(crate) async fn exit_review_mode(
|
||||
id: Some(REVIEW_USER_MESSAGE_ID.to_string()),
|
||||
role: "user".to_string(),
|
||||
content: vec![ContentItem::InputText { text: user_message }],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}],
|
||||
@@ -260,6 +261,7 @@ pub(crate) async fn exit_review_mode(
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: assistant_message,
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
|
||||
@@ -685,6 +685,7 @@ mod tests {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -696,6 +697,7 @@ mod tests {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
|
||||
@@ -1717,6 +1717,7 @@ mod tests {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "materialized".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
})]),
|
||||
|
||||
@@ -258,6 +258,7 @@ mod tests {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "hello".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}));
|
||||
@@ -271,6 +272,7 @@ mod tests {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: String::new(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}));
|
||||
|
||||
@@ -633,6 +633,7 @@ pub fn user_message_item(text: &str) -> ResponseItem {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
|
||||
@@ -105,6 +105,7 @@ async fn responses_stream_includes_subagent_header_on_review() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "hello".into(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}];
|
||||
@@ -218,6 +219,7 @@ async fn responses_stream_includes_subagent_header_on_other() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "hello".into(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}];
|
||||
@@ -330,6 +332,7 @@ async fn responses_respects_model_info_overrides_from_config() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "hello".into(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}];
|
||||
|
||||
@@ -177,6 +177,7 @@ async fn resume_includes_initial_messages_and_sends_prior_items() {
|
||||
content: vec![codex_protocol::models::ContentItem::InputText {
|
||||
text: "resumed user message".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -199,6 +200,7 @@ async fn resume_includes_initial_messages_and_sends_prior_items() {
|
||||
content: vec![codex_protocol::models::ContentItem::OutputText {
|
||||
text: "resumed system instruction".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -221,6 +223,7 @@ async fn resume_includes_initial_messages_and_sends_prior_items() {
|
||||
content: vec![codex_protocol::models::ContentItem::OutputText {
|
||||
text: "resumed assistant message".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: Some(MessagePhase::Commentary),
|
||||
};
|
||||
@@ -400,6 +403,7 @@ async fn resume_replays_legacy_js_repl_image_rollout_shapes() {
|
||||
content: vec![ContentItem::InputImage {
|
||||
image_url: legacy_image_url.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}),
|
||||
@@ -1864,6 +1868,7 @@ async fn azure_responses_request_includes_store_and_reasoning_ids() {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "message".into(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
});
|
||||
|
||||
@@ -1470,6 +1470,7 @@ fn message_item(text: &str) -> ResponseItem {
|
||||
id: None,
|
||||
role: "user".into(),
|
||||
content: vec![ContentItem::InputText { text: text.into() }],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -1480,6 +1481,7 @@ fn assistant_message_item(id: &str, text: &str) -> ResponseItem {
|
||||
id: Some(id.to_string()),
|
||||
role: "assistant".into(),
|
||||
content: vec![ContentItem::OutputText { text: text.into() }],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
|
||||
@@ -1586,6 +1586,7 @@ async fn auto_compact_runs_after_resume_when_token_usage_is_over_limit() {
|
||||
content: vec![codex_protocol::models::ContentItem::OutputText {
|
||||
text: remote_summary.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -2795,6 +2796,7 @@ async fn auto_compact_counts_encrypted_reasoning_before_last_user() {
|
||||
content: vec![codex_protocol::models::ContentItem::OutputText {
|
||||
text: "REMOTE_COMPACT_SUMMARY".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -2915,6 +2917,7 @@ async fn auto_compact_runs_when_reasoning_header_clears_between_turns() {
|
||||
content: vec![codex_protocol::models::ContentItem::OutputText {
|
||||
text: "REMOTE_COMPACT_SUMMARY".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
|
||||
@@ -1101,6 +1101,7 @@ async fn remote_compact_persists_replacement_history_in_rollout() -> Result<()>
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "COMPACTED_ASSISTANT_NOTE".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -1238,6 +1239,7 @@ async fn remote_compact_and_resume_refresh_stale_developer_instructions() -> Res
|
||||
content: vec![ContentItem::InputText {
|
||||
text: stale_developer_message.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -1370,6 +1372,7 @@ async fn remote_compact_refreshes_stale_developer_instructions_without_resume()
|
||||
content: vec![ContentItem::InputText {
|
||||
text: stale_developer_message.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
|
||||
@@ -157,6 +157,7 @@ async fn copy_paste_local_image_persists_rollout_request_shape() -> anyhow::Resu
|
||||
text: "pasted image".to_string(),
|
||||
},
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -240,6 +241,7 @@ async fn drag_drop_image_persists_rollout_request_shape() -> anyhow::Result<()>
|
||||
text: "dropped image".to_string(),
|
||||
},
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#![cfg(not(target_os = "windows"))]
|
||||
|
||||
use anyhow::Ok;
|
||||
use codex_core::features::Feature;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::config_types::Settings;
|
||||
@@ -35,6 +36,7 @@ use core_test_support::test_codex::test_codex;
|
||||
use core_test_support::wait_for_event;
|
||||
use core_test_support::wait_for_event_match;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::Value;
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn user_message_item_is_emitted() -> anyhow::Result<()> {
|
||||
@@ -42,7 +44,15 @@ async fn user_message_item_is_emitted() -> anyhow::Result<()> {
|
||||
|
||||
let server = start_mock_server().await;
|
||||
|
||||
let TestCodex { codex, .. } = test_codex().build(&server).await?;
|
||||
let TestCodex { codex, .. } = test_codex()
|
||||
.with_config(|config| {
|
||||
config
|
||||
.features
|
||||
.enable(Feature::UserMessageTypeMetadata)
|
||||
.expect("feature flag should be enabled for this test");
|
||||
})
|
||||
.build(&server)
|
||||
.await?;
|
||||
|
||||
let first_response = sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]);
|
||||
mount_sse_once(&server, first_response).await;
|
||||
@@ -105,6 +115,28 @@ async fn user_message_item_is_emitted() -> anyhow::Result<()> {
|
||||
.await;
|
||||
assert_eq!(legacy_message.message, "please inspect sample.txt");
|
||||
assert_eq!(legacy_message.text_elements, text_elements);
|
||||
|
||||
let requests = server.received_requests().await.unwrap_or_default();
|
||||
assert_eq!(requests.len(), 1);
|
||||
let body: Value = requests[0].body_json().expect("valid JSON request body");
|
||||
let input = body
|
||||
.get("input")
|
||||
.and_then(Value::as_array)
|
||||
.expect("request input array");
|
||||
let user_message = input
|
||||
.iter()
|
||||
.find(|item| {
|
||||
item.get("type").and_then(Value::as_str) == Some("message")
|
||||
&& item.get("role").and_then(Value::as_str) == Some("user")
|
||||
})
|
||||
.expect("user message input item");
|
||||
assert_eq!(
|
||||
user_message
|
||||
.get("metadata")
|
||||
.and_then(|metadata| metadata.get("user_message_type"))
|
||||
.and_then(Value::as_str),
|
||||
Some("prompt")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -530,6 +530,7 @@ async fn review_input_isolated_from_parent_history() {
|
||||
content: vec![codex_protocol::models::ContentItem::InputText {
|
||||
text: "parent: earlier user message".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -550,6 +551,7 @@ async fn review_input_isolated_from_parent_history() {
|
||||
content: vec![codex_protocol::models::ContentItem::OutputText {
|
||||
text: "parent: assistant reply".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ use ts_rs::TS;
|
||||
|
||||
use crate::config_types::CollaborationMode;
|
||||
use crate::config_types::SandboxMode;
|
||||
use crate::items::UserMessageType;
|
||||
use crate::protocol::AskForApproval;
|
||||
use crate::protocol::COLLABORATION_MODE_CLOSE_TAG;
|
||||
use crate::protocol::COLLABORATION_MODE_OPEN_TAG;
|
||||
@@ -250,6 +251,13 @@ pub enum ContentItem {
|
||||
OutputText { text: String },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, TS)]
|
||||
pub struct ResponseItemMetadata {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub user_message_type: Option<UserMessageType>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ImageDetail {
|
||||
@@ -284,6 +292,9 @@ pub enum ResponseItem {
|
||||
id: Option<String>,
|
||||
role: String,
|
||||
content: Vec<ContentItem>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
metadata: Option<ResponseItemMetadata>,
|
||||
// Do not use directly, no available consistently across all providers.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
@@ -711,6 +722,7 @@ impl From<DeveloperInstructions> for ResponseItem {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: di.into_text(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -870,6 +882,7 @@ impl From<ResponseInputItem> for ResponseItem {
|
||||
role,
|
||||
content,
|
||||
id: None,
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
|
||||
@@ -2365,6 +2365,7 @@ impl From<CompactedItem> for ResponseItem {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: value.message,
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
|
||||
@@ -165,6 +165,7 @@ mod tests {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "hello from response item".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user