mirror of
https://github.com/openai/codex.git
synced 2026-04-08 06:44:58 +00:00
Compare commits
39 Commits
dev/window
...
rhan/surfa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f354fa7124 | ||
|
|
810773ab86 | ||
|
|
30cd30a6e4 | ||
|
|
632ae61296 | ||
|
|
56ca2b539a | ||
|
|
13c91bde2e | ||
|
|
1db74b7287 | ||
|
|
ddab859227 | ||
|
|
6f6fb8c017 | ||
|
|
5590153fac | ||
|
|
73559b5898 | ||
|
|
3bce713f76 | ||
|
|
70972adbb9 | ||
|
|
bc6d8e294a | ||
|
|
1bfbb86ce1 | ||
|
|
892a00b3ed | ||
|
|
09f6d84504 | ||
|
|
6bebd3f3fb | ||
|
|
7bf187b172 | ||
|
|
2fc35f8b6b | ||
|
|
1879c53d6e | ||
|
|
56b0fe29fd | ||
|
|
445a6433a3 | ||
|
|
b2ded6f1e9 | ||
|
|
66c0851437 | ||
|
|
e9bf09ba09 | ||
|
|
f9570b714c | ||
|
|
8c019789b3 | ||
|
|
13fee8b4dc | ||
|
|
d8b457c841 | ||
|
|
1c435e54eb | ||
|
|
d2a4a8bbd7 | ||
|
|
0238699f48 | ||
|
|
95027497d4 | ||
|
|
6204ce60d0 | ||
|
|
2fa740749e | ||
|
|
9e058f1bc5 | ||
|
|
fcc2d8be21 | ||
|
|
79d8f99a7f |
@@ -1500,6 +1500,16 @@
|
||||
],
|
||||
"writeOnly": true
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMessageMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"phase": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -1950,6 +1960,28 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ResponseItemMessageMetadata": {
|
||||
"properties": {
|
||||
"metadata_id": {
|
||||
"description": "Client-visible metadata ID generated by Codex for this item.",
|
||||
"type": "string"
|
||||
},
|
||||
"user_message_type": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/UserMessageType"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"metadata_id"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ResponsesApiWebSearchAction": {
|
||||
"oneOf": [
|
||||
{
|
||||
@@ -3330,6 +3362,14 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"UserMessageType": {
|
||||
"enum": [
|
||||
"prompt",
|
||||
"prompt_steering",
|
||||
"prompt_queued"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"WindowsSandboxSetupMode": {
|
||||
"enum": [
|
||||
"elevated",
|
||||
|
||||
@@ -10275,6 +10275,16 @@
|
||||
],
|
||||
"writeOnly": true
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/ResponseItemMessageMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"phase": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -10725,6 +10735,28 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ResponseItemMessageMetadata": {
|
||||
"properties": {
|
||||
"metadata_id": {
|
||||
"description": "Client-visible metadata ID generated by Codex for this item.",
|
||||
"type": "string"
|
||||
},
|
||||
"user_message_type": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/UserMessageType"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"metadata_id"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ResponsesApiWebSearchAction": {
|
||||
"oneOf": [
|
||||
{
|
||||
@@ -14370,6 +14402,14 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"UserMessageType": {
|
||||
"enum": [
|
||||
"prompt",
|
||||
"prompt_steering",
|
||||
"prompt_queued"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"Verbosity": {
|
||||
"description": "Controls output length/detail on GPT-5 models via the Responses API. Serialized with lowercase values to match the OpenAI API.",
|
||||
"enum": [
|
||||
|
||||
@@ -7023,6 +7023,16 @@
|
||||
],
|
||||
"writeOnly": true
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMessageMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"phase": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -7473,6 +7483,28 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ResponseItemMessageMetadata": {
|
||||
"properties": {
|
||||
"metadata_id": {
|
||||
"description": "Client-visible metadata ID generated by Codex for this item.",
|
||||
"type": "string"
|
||||
},
|
||||
"user_message_type": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/UserMessageType"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"metadata_id"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ResponsesApiWebSearchAction": {
|
||||
"oneOf": [
|
||||
{
|
||||
@@ -12130,6 +12162,14 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"UserMessageType": {
|
||||
"enum": [
|
||||
"prompt",
|
||||
"prompt_steering",
|
||||
"prompt_queued"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"Verbosity": {
|
||||
"description": "Controls output length/detail on GPT-5 models via the Responses API. Serialized with lowercase values to match the OpenAI API.",
|
||||
"enum": [
|
||||
|
||||
@@ -348,6 +348,16 @@
|
||||
],
|
||||
"writeOnly": true
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMessageMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"phase": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -798,6 +808,28 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ResponseItemMessageMetadata": {
|
||||
"properties": {
|
||||
"metadata_id": {
|
||||
"description": "Client-visible metadata ID generated by Codex for this item.",
|
||||
"type": "string"
|
||||
},
|
||||
"user_message_type": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/UserMessageType"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"metadata_id"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ResponsesApiWebSearchAction": {
|
||||
"oneOf": [
|
||||
{
|
||||
@@ -898,6 +930,14 @@
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"UserMessageType": {
|
||||
"enum": [
|
||||
"prompt",
|
||||
"prompt_steering",
|
||||
"prompt_queued"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
|
||||
@@ -414,6 +414,16 @@
|
||||
],
|
||||
"writeOnly": true
|
||||
},
|
||||
"metadata": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResponseItemMessageMetadata"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"phase": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -864,6 +874,28 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ResponseItemMessageMetadata": {
|
||||
"properties": {
|
||||
"metadata_id": {
|
||||
"description": "Client-visible metadata ID generated by Codex for this item.",
|
||||
"type": "string"
|
||||
},
|
||||
"user_message_type": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/UserMessageType"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"metadata_id"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ResponsesApiWebSearchAction": {
|
||||
"oneOf": [
|
||||
{
|
||||
@@ -979,6 +1011,14 @@
|
||||
"flex"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"UserMessageType": {
|
||||
"enum": [
|
||||
"prompt",
|
||||
"prompt_steering",
|
||||
"prompt_queued"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"description": "There are three ways to resume a thread: 1. By thread_id: load the thread from disk by thread_id and resume it. 2. By history: instantiate the thread from memory and resume it. 3. By path: load the thread from disk by path and resume it.\n\nThe precedence is: history > path > thread_id. If using history or path, the thread_id param will be ignored.\n\nPrefer using thread_id whenever possible.",
|
||||
|
||||
@@ -9,9 +9,10 @@ import type { LocalShellStatus } from "./LocalShellStatus";
|
||||
import type { MessagePhase } from "./MessagePhase";
|
||||
import type { ReasoningItemContent } from "./ReasoningItemContent";
|
||||
import type { ReasoningItemReasoningSummary } from "./ReasoningItemReasoningSummary";
|
||||
import type { ResponseItemMessageMetadata } from "./ResponseItemMessageMetadata";
|
||||
import type { WebSearchAction } from "./WebSearchAction";
|
||||
|
||||
export type ResponseItem = { "type": "message", role: string, content: Array<ContentItem>, end_turn?: boolean, phase?: MessagePhase, } | { "type": "reasoning", summary: Array<ReasoningItemReasoningSummary>, content?: Array<ReasoningItemContent>, encrypted_content: string | null, } | { "type": "local_shell_call",
|
||||
export type ResponseItem = { "type": "message", role: string, content: Array<ContentItem>, metadata?: ResponseItemMessageMetadata, end_turn?: boolean, phase?: MessagePhase, } | { "type": "reasoning", summary: Array<ReasoningItemReasoningSummary>, content?: Array<ReasoningItemContent>, encrypted_content: string | null, } | { "type": "local_shell_call",
|
||||
/**
|
||||
* Set when using the Responses API.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { UserMessageType } from "./UserMessageType";
|
||||
|
||||
export type ResponseItemMessageMetadata = { user_message_type?: UserMessageType,
|
||||
/**
|
||||
* Client-visible metadata ID generated by Codex for this item.
|
||||
*/
|
||||
metadata_id: string, };
|
||||
@@ -0,0 +1,5 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type UserMessageType = "prompt" | "prompt_steering" | "prompt_queued";
|
||||
@@ -60,6 +60,7 @@ export type { RequestId } from "./RequestId";
|
||||
export type { Resource } from "./Resource";
|
||||
export type { ResourceTemplate } from "./ResourceTemplate";
|
||||
export type { ResponseItem } from "./ResponseItem";
|
||||
export type { ResponseItemMessageMetadata } from "./ResponseItemMessageMetadata";
|
||||
export type { ReviewDecision } from "./ReviewDecision";
|
||||
export type { ServerNotification } from "./ServerNotification";
|
||||
export type { ServerRequest } from "./ServerRequest";
|
||||
@@ -69,6 +70,7 @@ export type { Settings } from "./Settings";
|
||||
export type { SubAgentSource } from "./SubAgentSource";
|
||||
export type { ThreadId } from "./ThreadId";
|
||||
export type { Tool } from "./Tool";
|
||||
export type { UserMessageType } from "./UserMessageType";
|
||||
export type { Verbosity } from "./Verbosity";
|
||||
export type { WebSearchAction } from "./WebSearchAction";
|
||||
export type { WebSearchContextSize } from "./WebSearchContextSize";
|
||||
|
||||
@@ -2795,6 +2795,7 @@ mod tests {
|
||||
content: vec![codex_protocol::models::ContentItem::InputText {
|
||||
text: "plain text".into(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}),
|
||||
|
||||
@@ -129,6 +129,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,
|
||||
},
|
||||
|
||||
@@ -768,6 +768,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,
|
||||
}]),
|
||||
@@ -1596,6 +1597,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,
|
||||
}],
|
||||
|
||||
@@ -407,6 +407,9 @@
|
||||
"include_apply_patch_tool": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"item_metadata": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"js_repl": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -2013,6 +2016,9 @@
|
||||
"include_apply_patch_tool": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"item_metadata": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"js_repl": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
||||
@@ -38,6 +38,7 @@ impl InterAgentInstruction {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: self.as_text(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ async fn build_arc_monitor_request_includes_relevant_history_and_null_policies()
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "first request".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}],
|
||||
@@ -87,6 +88,7 @@ async fn build_arc_monitor_request_includes_relevant_history_and_null_policies()
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "commentary".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: Some(MessagePhase::Commentary),
|
||||
}],
|
||||
@@ -101,6 +103,7 @@ async fn build_arc_monitor_request_includes_relevant_history_and_null_policies()
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "final response".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: Some(MessagePhase::FinalAnswer),
|
||||
}],
|
||||
@@ -115,6 +118,7 @@ async fn build_arc_monitor_request_includes_relevant_history_and_null_policies()
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "latest request".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}],
|
||||
@@ -270,6 +274,7 @@ async fn monitor_action_posts_expected_arc_request() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "please run the tool".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}],
|
||||
@@ -351,6 +356,7 @@ async fn monitor_action_uses_env_url_and_token_overrides() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "please run the tool".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}],
|
||||
@@ -421,6 +427,7 @@ async fn monitor_action_rejects_legacy_response_fields() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "please run the tool".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}],
|
||||
|
||||
@@ -3,7 +3,10 @@ use codex_api::common::OpenAiVerbosity;
|
||||
use codex_api::common::TextControls;
|
||||
use codex_api::create_text_param_for_request;
|
||||
use codex_protocol::config_types::ServiceTier;
|
||||
use codex_protocol::models::ContentItem;
|
||||
use codex_protocol::models::FunctionCallOutputPayload;
|
||||
use codex_protocol::models::ResponseItemMessageMetadata;
|
||||
use codex_protocol::models::UserMessageType;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
@@ -198,6 +201,76 @@ fn reserializes_shell_outputs_for_function_and_custom_tool_calls() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn formatted_input_preserves_message_metadata_id_when_disabled() {
|
||||
let prompt = Prompt {
|
||||
input: vec![ResponseItem::Message {
|
||||
id: Some("msg_123".to_string()),
|
||||
role: "user".to_string(),
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "hello".to_string(),
|
||||
}],
|
||||
metadata: Some(ResponseItemMessageMetadata {
|
||||
user_message_type: Some(UserMessageType::Prompt),
|
||||
metadata_id: "2585a800-7d93-4f52-8648-d9cb39f413d2".to_string(),
|
||||
}),
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let formatted = prompt.get_formatted_input();
|
||||
assert_eq!(formatted.len(), 1);
|
||||
|
||||
match &formatted[0] {
|
||||
ResponseItem::Message { metadata, .. } => {
|
||||
let metadata = metadata.as_ref().expect("metadata should be present");
|
||||
assert_eq!(metadata.user_message_type, Some(UserMessageType::Prompt));
|
||||
assert_eq!(
|
||||
metadata.metadata_id,
|
||||
"2585a800-7d93-4f52-8648-d9cb39f413d2".to_string()
|
||||
);
|
||||
}
|
||||
other => panic!("expected message item, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn formatted_input_preserves_existing_message_metadata_id() {
|
||||
let prompt = Prompt {
|
||||
input: vec![ResponseItem::Message {
|
||||
id: Some("msg_123".to_string()),
|
||||
role: "user".to_string(),
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "hello".to_string(),
|
||||
}],
|
||||
metadata: Some(ResponseItemMessageMetadata {
|
||||
user_message_type: Some(UserMessageType::Prompt),
|
||||
metadata_id: "2585a800-7d93-4f52-8648-d9cb39f413d2".to_string(),
|
||||
}),
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let formatted = prompt.get_formatted_input();
|
||||
assert_eq!(formatted.len(), 1);
|
||||
|
||||
match &formatted[0] {
|
||||
ResponseItem::Message { metadata, .. } => {
|
||||
let metadata = metadata.as_ref().expect("metadata should be present");
|
||||
assert_eq!(metadata.user_message_type, Some(UserMessageType::Prompt));
|
||||
assert_eq!(
|
||||
Some(metadata.metadata_id.as_str()),
|
||||
Some("2585a800-7d93-4f52-8648-d9cb39f413d2")
|
||||
);
|
||||
}
|
||||
other => panic!("expected message item, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tool_search_output_namespace_serializes_with_deferred_child_tools() {
|
||||
let namespace = tools::ToolSearchOutputTool::Namespace(tools::ResponsesApiNamespace {
|
||||
|
||||
@@ -362,6 +362,7 @@ use codex_protocol::models::ContentItem;
|
||||
use codex_protocol::models::DeveloperInstructions;
|
||||
use codex_protocol::models::ResponseInputItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::models::ResponseItemMessageMetadata;
|
||||
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
|
||||
use codex_protocol::protocol::CodexErrorInfo;
|
||||
use codex_protocol::protocol::InitialHistory;
|
||||
@@ -2760,6 +2761,7 @@ impl Session {
|
||||
.inject_response_items(vec![ResponseInputItem::Message {
|
||||
role: "developer".to_string(),
|
||||
content: vec![ContentItem::InputText { text }],
|
||||
metadata: None,
|
||||
}])
|
||||
.await
|
||||
.is_err()
|
||||
@@ -2857,6 +2859,7 @@ impl Session {
|
||||
.inject_response_items(vec![ResponseInputItem::Message {
|
||||
role: "developer".to_string(),
|
||||
content: vec![ContentItem::InputText { text }],
|
||||
metadata: None,
|
||||
}])
|
||||
.await
|
||||
.is_err()
|
||||
@@ -3309,9 +3312,11 @@ impl Session {
|
||||
turn_context: &TurnContext,
|
||||
items: &[ResponseItem],
|
||||
) {
|
||||
self.record_into_history(items, turn_context).await;
|
||||
self.persist_rollout_response_items(items).await;
|
||||
self.send_raw_response_items(turn_context, items).await;
|
||||
let items = self.prepare_history_items(items);
|
||||
self.record_into_history_prepared(&items, turn_context)
|
||||
.await;
|
||||
self.persist_rollout_response_items(&items).await;
|
||||
self.send_raw_response_items(turn_context, &items).await;
|
||||
}
|
||||
|
||||
/// Append ResponseItems to the in-memory conversation history only.
|
||||
@@ -3319,6 +3324,47 @@ impl Session {
|
||||
&self,
|
||||
items: &[ResponseItem],
|
||||
turn_context: &TurnContext,
|
||||
) {
|
||||
let items = self.prepare_history_items(items);
|
||||
self.record_into_history_prepared(&items, turn_context)
|
||||
.await;
|
||||
}
|
||||
|
||||
fn prepare_history_items(&self, items: &[ResponseItem]) -> Vec<ResponseItem> {
|
||||
if self.enabled(Feature::ItemMetadata) {
|
||||
items
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|item| match item {
|
||||
ResponseItem::Message {
|
||||
id,
|
||||
role,
|
||||
content,
|
||||
metadata,
|
||||
end_turn,
|
||||
phase,
|
||||
} => ResponseItem::Message {
|
||||
id,
|
||||
role,
|
||||
content,
|
||||
metadata: Some(metadata.unwrap_or_else(|| {
|
||||
ResponseItemMessageMetadata::new(/*user_message_type*/ None)
|
||||
})),
|
||||
end_turn,
|
||||
phase,
|
||||
},
|
||||
other => other,
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
items.to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
async fn record_into_history_prepared(
|
||||
&self,
|
||||
items: &[ResponseItem],
|
||||
turn_context: &TurnContext,
|
||||
) {
|
||||
let mut state = self.state.lock().await;
|
||||
state.record_items(items.iter(), turn_context.truncation_policy);
|
||||
@@ -3334,6 +3380,7 @@ impl Session {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: format!("Warning: {}", message.into()),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ use crate::models_manager::model_info;
|
||||
use crate::shell::default_user_shell;
|
||||
use crate::tools::format_exec_output_str;
|
||||
|
||||
use codex_features::Feature;
|
||||
use codex_features::Features;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
@@ -128,6 +129,7 @@ fn user_message(text: &str) -> ResponseItem {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -140,6 +142,7 @@ fn assistant_message(text: &str) -> ResponseItem {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -152,6 +155,7 @@ fn skill_message(text: &str) -> ResponseItem {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -883,6 +887,7 @@ async fn reconstruct_history_uses_replacement_history_verbatim() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "summary".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -894,6 +899,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,
|
||||
},
|
||||
@@ -4007,6 +4013,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,
|
||||
};
|
||||
@@ -4370,6 +4377,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,
|
||||
}])
|
||||
.await
|
||||
.expect("inject pending input into active turn");
|
||||
@@ -4383,6 +4391,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,
|
||||
};
|
||||
@@ -4577,6 +4586,37 @@ async fn steer_input_returns_active_turn_id() {
|
||||
assert!(sess.has_pending_input().await);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn record_into_history_generates_message_metadata_id_when_item_metadata_enabled() {
|
||||
let (mut sess, tc) = make_session_and_context().await;
|
||||
let _ = sess.features.enable(Feature::ItemMetadata);
|
||||
|
||||
let item = ResponseItem::Message {
|
||||
id: Some("msg_123".to_string()),
|
||||
role: "user".to_string(),
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "hello".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
|
||||
sess.record_into_history(std::slice::from_ref(&item), &tc)
|
||||
.await;
|
||||
|
||||
let history = sess.state.lock().await.clone_history();
|
||||
let [ResponseItem::Message { metadata, .. }] = history.raw_items() else {
|
||||
panic!("expected a single message item in history");
|
||||
};
|
||||
|
||||
let metadata_id = metadata
|
||||
.as_ref()
|
||||
.map(|metadata| metadata.metadata_id.as_str())
|
||||
.expect("metadata_id should be generated when item metadata is enabled");
|
||||
uuid::Uuid::parse_str(metadata_id).expect("metadata_id should be valid");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn prepend_pending_input_keeps_older_tail_ahead_of_newer_input() {
|
||||
let (sess, tc, _rx) = make_session_and_context_with_rx().await;
|
||||
@@ -4599,18 +4639,21 @@ async fn prepend_pending_input_keeps_older_tail_ahead_of_newer_input() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "blocked queued prompt".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
};
|
||||
let later = ResponseInputItem::Message {
|
||||
role: "user".to_string(),
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "later queued prompt".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
};
|
||||
let newer = ResponseInputItem::Message {
|
||||
role: "user".to_string(),
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "newer queued prompt".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
};
|
||||
|
||||
sess.inject_response_items(vec![blocked.clone(), later.clone()])
|
||||
@@ -4845,6 +4888,7 @@ async fn sample_rollout(
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "first user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -4860,6 +4904,7 @@ async fn sample_rollout(
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "assistant reply one".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -4887,6 +4932,7 @@ async fn sample_rollout(
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "second user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -4902,6 +4948,7 @@ async fn sample_rollout(
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "assistant reply two".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -4929,6 +4976,7 @@ async fn sample_rollout(
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "third user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -4944,6 +4992,7 @@ async fn sample_rollout(
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "assistant reply three".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
|
||||
@@ -260,6 +260,7 @@ async fn process_compacted_history_preserves_separate_guardian_developer_message
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "stale developer message".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -269,6 +270,7 @@ async fn process_compacted_history_preserves_separate_guardian_developer_message
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "summary".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
|
||||
@@ -128,6 +128,7 @@ impl CodexThread {
|
||||
id: None,
|
||||
role: "user".to_string(),
|
||||
content: vec![ContentItem::InputText { text: message }],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -264,9 +265,15 @@ impl CodexThread {
|
||||
|
||||
fn pending_message_input_item(message: &ResponseItem) -> CodexResult<ResponseInputItem> {
|
||||
match message {
|
||||
ResponseItem::Message { role, content, .. } => Ok(ResponseInputItem::Message {
|
||||
ResponseItem::Message {
|
||||
role,
|
||||
content,
|
||||
metadata,
|
||||
..
|
||||
} => Ok(ResponseInputItem::Message {
|
||||
role: role.clone(),
|
||||
content: content.clone(),
|
||||
metadata: metadata.clone(),
|
||||
}),
|
||||
_ => Err(CodexErr::InvalidRequest(
|
||||
"append_message only supports ResponseItem::Message".to_string(),
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -59,6 +59,7 @@ fn collect_user_messages_extracts_user_text_only() {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "ignored".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -68,6 +69,7 @@ fn collect_user_messages_extracts_user_text_only() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "first".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -93,6 +95,7 @@ do things
|
||||
</INSTRUCTIONS>"#
|
||||
.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -102,6 +105,7 @@ do things
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "<ENVIRONMENT_CONTEXT>cwd=/tmp</ENVIRONMENT_CONTEXT>".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -111,6 +115,7 @@ do things
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "real user message".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -194,6 +199,7 @@ async fn process_compacted_history_replaces_developer_messages() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "stale permissions".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -203,6 +209,7 @@ async fn process_compacted_history_replaces_developer_messages() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "summary".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -212,6 +219,7 @@ async fn process_compacted_history_replaces_developer_messages() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "stale personality".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -224,6 +232,7 @@ async fn process_compacted_history_replaces_developer_messages() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "summary".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
});
|
||||
@@ -238,6 +247,7 @@ async fn process_compacted_history_reinjects_full_initial_context() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "summary".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}];
|
||||
@@ -249,6 +259,7 @@ async fn process_compacted_history_reinjects_full_initial_context() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "summary".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
});
|
||||
@@ -269,6 +280,7 @@ keep me updated
|
||||
</INSTRUCTIONS>"#
|
||||
.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -282,6 +294,7 @@ keep me updated
|
||||
</environment_context>"#
|
||||
.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -295,6 +308,7 @@ keep me updated
|
||||
</turn_aborted>"#
|
||||
.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -304,6 +318,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "summary".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -313,6 +328,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "stale developer instructions".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -325,6 +341,7 @@ keep me updated
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "summary".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
});
|
||||
@@ -340,6 +357,7 @@ async fn process_compacted_history_inserts_context_before_last_real_user_message
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "older user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -349,6 +367,7 @@ async fn process_compacted_history_inserts_context_before_last_real_user_message
|
||||
content: vec![ContentItem::InputText {
|
||||
text: format!("{SUMMARY_PREFIX}\nsummary text"),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -358,6 +377,7 @@ async fn process_compacted_history_inserts_context_before_last_real_user_message
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "latest user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -372,6 +392,7 @@ async fn process_compacted_history_inserts_context_before_last_real_user_message
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "older user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -381,6 +402,7 @@ async fn process_compacted_history_inserts_context_before_last_real_user_message
|
||||
content: vec![ContentItem::InputText {
|
||||
text: format!("{SUMMARY_PREFIX}\nsummary text"),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -392,6 +414,7 @@ async fn process_compacted_history_inserts_context_before_last_real_user_message
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "latest user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
});
|
||||
@@ -406,6 +429,7 @@ async fn process_compacted_history_reinjects_model_switch_message() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "summary".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}];
|
||||
@@ -436,6 +460,7 @@ async fn process_compacted_history_reinjects_model_switch_message() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "summary".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
});
|
||||
@@ -451,6 +476,7 @@ fn insert_initial_context_before_last_real_user_or_summary_keeps_summary_last()
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "older user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -460,6 +486,7 @@ fn insert_initial_context_before_last_real_user_or_summary_keeps_summary_last()
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "latest user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -469,6 +496,7 @@ fn insert_initial_context_before_last_real_user_or_summary_keeps_summary_last()
|
||||
content: vec![ContentItem::InputText {
|
||||
text: format!("{SUMMARY_PREFIX}\nsummary text"),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -479,6 +507,7 @@ fn insert_initial_context_before_last_real_user_or_summary_keeps_summary_last()
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "fresh permissions".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}];
|
||||
@@ -492,6 +521,7 @@ fn insert_initial_context_before_last_real_user_or_summary_keeps_summary_last()
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "older user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -501,6 +531,7 @@ fn insert_initial_context_before_last_real_user_or_summary_keeps_summary_last()
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "fresh permissions".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -510,6 +541,7 @@ fn insert_initial_context_before_last_real_user_or_summary_keeps_summary_last()
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "latest user".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -519,6 +551,7 @@ fn insert_initial_context_before_last_real_user_or_summary_keeps_summary_last()
|
||||
content: vec![ContentItem::InputText {
|
||||
text: format!("{SUMMARY_PREFIX}\nsummary text"),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -537,6 +570,7 @@ fn insert_initial_context_before_last_real_user_or_summary_keeps_compaction_last
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "fresh permissions".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}];
|
||||
@@ -550,6 +584,7 @@ fn insert_initial_context_before_last_real_user_or_summary_keeps_compaction_last
|
||||
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,
|
||||
}
|
||||
@@ -65,6 +66,7 @@ fn user_msg(text: &str) -> ResponseItem {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -77,6 +79,7 @@ fn user_input_text_msg(text: &str) -> ResponseItem {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -133,6 +136,7 @@ fn filters_non_api_messages() {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "ignored".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -164,6 +168,7 @@ fn filters_non_api_messages() {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "hi".to_string()
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -173,6 +178,7 @@ fn filters_non_api_messages() {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "hello".to_string()
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -307,6 +313,7 @@ fn for_prompt_strips_images_when_model_does_not_support_images() {
|
||||
text: "caption".to_string(),
|
||||
},
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -370,6 +377,7 @@ fn for_prompt_strips_images_when_model_does_not_support_images() {
|
||||
text: "caption".to_string(),
|
||||
},
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -428,6 +436,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,
|
||||
}]);
|
||||
@@ -456,6 +465,7 @@ fn for_prompt_preserves_image_generation_calls_when_images_are_supported() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "hi".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -476,6 +486,7 @@ fn for_prompt_preserves_image_generation_calls_when_images_are_supported() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "hi".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -492,6 +503,7 @@ fn for_prompt_clears_image_generation_result_when_images_are_unsupported() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "generate a lobster".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -512,6 +524,7 @@ fn for_prompt_clears_image_generation_result_when_images_are_unsupported() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "generate a lobster".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -668,6 +681,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,
|
||||
}];
|
||||
@@ -1528,6 +1542,7 @@ fn image_data_url_payload_does_not_dominate_message_estimate() {
|
||||
},
|
||||
ContentItem::InputImage { image_url },
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -1537,6 +1552,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,
|
||||
};
|
||||
@@ -1610,6 +1626,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,
|
||||
};
|
||||
@@ -1641,6 +1658,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,
|
||||
};
|
||||
@@ -1679,6 +1697,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,
|
||||
};
|
||||
@@ -1710,6 +1729,7 @@ fn multiple_inline_images_apply_multiple_fixed_costs() {
|
||||
image_url: image_url_two,
|
||||
},
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -1793,6 +1813,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,
|
||||
};
|
||||
|
||||
@@ -180,6 +180,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,
|
||||
})
|
||||
|
||||
@@ -59,6 +59,7 @@ impl ContextualUserFragmentDefinition {
|
||||
id: None,
|
||||
role: "user".to_string(),
|
||||
content: vec![ContentItem::InputText { text }],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ fn parses_user_message_with_text_and_two_images() {
|
||||
image_url: img2.clone(),
|
||||
},
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -74,6 +75,7 @@ fn skips_local_image_label_text() {
|
||||
text: user_text.clone(),
|
||||
},
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -153,6 +155,7 @@ fn skips_unnamed_image_label_text() {
|
||||
text: user_text.clone(),
|
||||
},
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -183,6 +186,7 @@ fn skips_user_instructions_and_env() {
|
||||
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,
|
||||
},
|
||||
@@ -192,6 +196,7 @@ fn skips_user_instructions_and_env() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "<environment_context>test_text</environment_context>".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -201,6 +206,7 @@ fn skips_user_instructions_and_env() {
|
||||
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,
|
||||
},
|
||||
@@ -211,6 +217,7 @@ fn skips_user_instructions_and_env() {
|
||||
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,
|
||||
},
|
||||
@@ -220,6 +227,7 @@ fn skips_user_instructions_and_env() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "<user_shell_command>echo 42</user_shell_command>".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -236,6 +244,7 @@ fn skips_user_instructions_and_env() {
|
||||
.to_string(),
|
||||
},
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -287,6 +296,7 @@ fn parses_hook_prompt_and_hides_other_contextual_fragments() {
|
||||
.to_string(),
|
||||
},
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -316,6 +326,7 @@ fn parses_agent_message() {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "Hello from Codex".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
|
||||
@@ -86,6 +86,7 @@ async fn seed_guardian_parent_history(session: &Arc<Session>, turn: &Arc<TurnCon
|
||||
text: "Please check the repo visibility and push the docs fix if needed."
|
||||
.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -109,6 +110,7 @@ async fn seed_guardian_parent_history(session: &Arc<Session>, turn: &Arc<TurnCon
|
||||
text: "The repo is public; I now need approval to push the docs fix."
|
||||
.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
@@ -162,6 +164,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,
|
||||
},
|
||||
@@ -171,6 +174,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,
|
||||
},
|
||||
@@ -197,6 +201,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,
|
||||
},
|
||||
@@ -219,6 +224,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,
|
||||
},
|
||||
|
||||
@@ -331,6 +331,7 @@ mod job {
|
||||
&rollout_contents,
|
||||
)?,
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}],
|
||||
@@ -487,8 +488,10 @@ mod job {
|
||||
id,
|
||||
role,
|
||||
content,
|
||||
metadata,
|
||||
end_turn,
|
||||
phase,
|
||||
..
|
||||
} = item
|
||||
else {
|
||||
return should_persist_response_item_for_memories(item).then(|| item.clone());
|
||||
@@ -515,6 +518,7 @@ mod job {
|
||||
id: id.clone(),
|
||||
role: role.clone(),
|
||||
content,
|
||||
metadata: metadata.clone(),
|
||||
end_turn: *end_turn,
|
||||
phase: phase.clone(),
|
||||
})
|
||||
|
||||
@@ -22,6 +22,7 @@ fn serializes_memory_rollout_with_agents_removed_but_environment_kept() {
|
||||
text: "<environment_context>\n<cwd>/tmp</cwd>\n</environment_context>".to_string(),
|
||||
},
|
||||
],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -32,6 +33,7 @@ fn serializes_memory_rollout_with_agents_removed_but_environment_kept() {
|
||||
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,
|
||||
};
|
||||
@@ -42,6 +44,7 @@ fn serializes_memory_rollout_with_agents_removed_but_environment_kept() {
|
||||
text: "<subagent_notification>{\"agent_id\":\"a\",\"status\":\"completed\"}</subagent_notification>"
|
||||
.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
@@ -64,6 +67,7 @@ fn serializes_memory_rollout_with_agents_removed_but_environment_kept() {
|
||||
text: "<environment_context>\n<cwd>/tmp</cwd>\n</environment_context>"
|
||||
.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
},
|
||||
|
||||
@@ -1135,6 +1135,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,
|
||||
}),
|
||||
|
||||
@@ -13,6 +13,7 @@ fn user_msg(text: &str) -> ResponseItem {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -25,6 +26,7 @@ fn assistant_msg(text: &str) -> ResponseItem {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ fn assistant_output_text(text: &str) -> ResponseItem {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: Some(true),
|
||||
phase: None,
|
||||
}
|
||||
|
||||
@@ -464,6 +464,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,
|
||||
},
|
||||
|
||||
@@ -340,7 +340,16 @@ async fn persist_user_shell_output(
|
||||
}
|
||||
|
||||
let response_input_item = match output_item {
|
||||
ResponseItem::Message { role, content, .. } => ResponseInputItem::Message { role, content },
|
||||
ResponseItem::Message {
|
||||
role,
|
||||
content,
|
||||
metadata,
|
||||
..
|
||||
} => ResponseInputItem::Message {
|
||||
role,
|
||||
content,
|
||||
metadata,
|
||||
},
|
||||
_ => unreachable!("user shell command output record should always be a message"),
|
||||
};
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ fn user_msg(text: &str) -> ResponseItem {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -32,6 +33,7 @@ fn assistant_msg(text: &str) -> ResponseItem {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: text.to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
|
||||
@@ -1183,6 +1183,7 @@ async fn resume_agent_restores_closed_agent_and_accepts_send_input() {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "materialized".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
})]),
|
||||
|
||||
@@ -102,6 +102,7 @@ fn response_item_records_turn_ttft_for_first_output_signals() {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "hello".to_string(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}));
|
||||
@@ -115,6 +116,7 @@ fn response_item_records_turn_ttft_ignores_empty_non_output_items() {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: String::new(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}));
|
||||
|
||||
@@ -637,6 +637,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),
|
||||
};
|
||||
@@ -401,6 +404,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,
|
||||
}),
|
||||
@@ -1857,6 +1861,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,
|
||||
});
|
||||
|
||||
@@ -1629,6 +1629,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,
|
||||
}
|
||||
@@ -1639,6 +1640,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,
|
||||
}
|
||||
|
||||
@@ -1589,6 +1589,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,
|
||||
},
|
||||
@@ -2798,6 +2799,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,
|
||||
},
|
||||
@@ -2918,6 +2920,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,
|
||||
},
|
||||
|
||||
@@ -1124,6 +1124,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,
|
||||
},
|
||||
@@ -1261,6 +1262,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,
|
||||
},
|
||||
@@ -1393,6 +1395,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,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -166,6 +166,8 @@ pub enum Feature {
|
||||
ToolCallMcpElicitation,
|
||||
/// Enable personality selection in the TUI.
|
||||
Personality,
|
||||
/// Emit item-level metadata such as user message type and generated metadata IDs.
|
||||
ItemMetadata,
|
||||
/// Enable native artifact tools.
|
||||
Artifact,
|
||||
/// Enable Fast mode selection in the TUI and request layer.
|
||||
@@ -791,6 +793,12 @@ pub const FEATURES: &[FeatureSpec] = &[
|
||||
stage: Stage::Stable,
|
||||
default_enabled: true,
|
||||
},
|
||||
FeatureSpec {
|
||||
id: Feature::ItemMetadata,
|
||||
key: "item_metadata",
|
||||
stage: Stage::UnderDevelopment,
|
||||
default_enabled: false,
|
||||
},
|
||||
FeatureSpec {
|
||||
id: Feature::Artifact,
|
||||
key: "artifact",
|
||||
|
||||
@@ -147,6 +147,12 @@ fn image_detail_original_feature_is_under_development() {
|
||||
assert_eq!(Feature::ImageDetailOriginal.default_enabled(), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn item_metadata_is_under_development_and_disabled_by_default() {
|
||||
assert_eq!(Feature::ItemMetadata.stage(), Stage::UnderDevelopment);
|
||||
assert_eq!(Feature::ItemMetadata.default_enabled(), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn collab_is_legacy_alias_for_multi_agent() {
|
||||
assert_eq!(feature_for_key("multi_agent"), Some(Feature::Collab));
|
||||
|
||||
@@ -265,6 +265,7 @@ pub fn build_hook_prompt_message(fragments: &[HookPromptFragment]) -> Option<Res
|
||||
id: Some(uuid::Uuid::new_v4().to_string()),
|
||||
role: "user".to_string(),
|
||||
content,
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
})
|
||||
|
||||
@@ -229,6 +229,9 @@ pub enum ResponseInputItem {
|
||||
Message {
|
||||
role: String,
|
||||
content: Vec<ContentItem>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
metadata: Option<ResponseItemMessageMetadata>,
|
||||
},
|
||||
FunctionCallOutput {
|
||||
call_id: String,
|
||||
@@ -258,6 +261,33 @@ pub enum ResponseInputItem {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(rename_all = "snake_case")]
|
||||
pub enum UserMessageType {
|
||||
Prompt,
|
||||
PromptSteering,
|
||||
PromptQueued,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema, TS)]
|
||||
pub struct ResponseItemMessageMetadata {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub user_message_type: Option<UserMessageType>,
|
||||
/// Client-visible metadata ID generated by Codex for this item.
|
||||
pub metadata_id: String,
|
||||
}
|
||||
|
||||
impl ResponseItemMessageMetadata {
|
||||
pub fn new(user_message_type: Option<UserMessageType>) -> Self {
|
||||
Self {
|
||||
user_message_type,
|
||||
metadata_id: uuid::Uuid::new_v4().to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema, TS)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ContentItem {
|
||||
@@ -300,6 +330,9 @@ pub enum ResponseItem {
|
||||
id: Option<String>,
|
||||
role: String,
|
||||
content: Vec<ContentItem>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
metadata: Option<ResponseItemMessageMetadata>,
|
||||
// Do not use directly, no available consistently across all providers.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
@@ -865,6 +898,7 @@ impl From<DeveloperInstructions> for ResponseItem {
|
||||
content: vec![ContentItem::InputText {
|
||||
text: di.into_text(),
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
@@ -1013,9 +1047,14 @@ pub fn local_image_content_items_with_label_number(
|
||||
impl From<ResponseInputItem> for ResponseItem {
|
||||
fn from(item: ResponseInputItem) -> Self {
|
||||
match item {
|
||||
ResponseInputItem::Message { role, content } => Self::Message {
|
||||
ResponseInputItem::Message {
|
||||
role,
|
||||
content,
|
||||
metadata,
|
||||
} => Self::Message {
|
||||
role,
|
||||
content,
|
||||
metadata,
|
||||
id: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
@@ -1153,6 +1192,7 @@ impl From<Vec<UserInput>> for ResponseInputItem {
|
||||
UserInput::Skill { .. } | UserInput::Mention { .. } => Vec::new(), // Tool bodies are injected later in core
|
||||
})
|
||||
.collect::<Vec<ContentItem>>(),
|
||||
metadata: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3026,4 +3066,31 @@ mod tests {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn response_item_message_metadata_serializes_required_metadata_id() -> Result<()> {
|
||||
let metadata = ResponseItemMessageMetadata::new(Some(UserMessageType::Prompt));
|
||||
let serialized = serde_json::to_value(&metadata)?;
|
||||
let metadata = serialized.as_object().expect("metadata should be present");
|
||||
let metadata_id = metadata
|
||||
.get("metadata_id")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.expect("metadata_id should be present");
|
||||
|
||||
uuid::Uuid::parse_str(metadata_id).expect("metadata_id should be valid");
|
||||
assert_eq!(
|
||||
metadata.get("user_message_type"),
|
||||
Some(&serde_json::json!("prompt"))
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn response_item_message_metadata_new_generates_metadata_id() {
|
||||
let metadata = ResponseItemMessageMetadata::new(Some(UserMessageType::Prompt));
|
||||
|
||||
assert_eq!(metadata.user_message_type, Some(UserMessageType::Prompt));
|
||||
uuid::Uuid::parse_str(&metadata.metadata_id).expect("metadata_id should be valid");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2506,6 +2506,7 @@ impl From<CompactedItem> for ResponseItem {
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: value.message,
|
||||
}],
|
||||
metadata: None,
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
}
|
||||
|
||||
@@ -169,6 +169,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