chore: add phase to message responseitem (#10455)

### What

add wiring for `phase` field on `ResponseItem::Message` to lay
groundwork for differentiating model preambles and final messages.
currently optional.

follows pattern in #9698.

updated schemas with `just write-app-server-schema` so we can see type
changes.

### Tests
Updated existing tests for SSE parsing and hydrating from history
This commit is contained in:
sayan-oai
2026-02-02 18:52:26 -08:00
committed by GitHub
parent 0999fd82b9
commit fc05374344
46 changed files with 323 additions and 4 deletions

View File

@@ -29,6 +29,7 @@ use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::config_types::Settings;
use codex_protocol::config_types::Verbosity;
use codex_protocol::models::FunctionCallOutputPayload;
use codex_protocol::models::MessagePhase;
use codex_protocol::models::ReasoningItemContent;
use codex_protocol::models::ReasoningItemReasoningSummary;
use codex_protocol::models::WebSearchAction;
@@ -197,6 +198,7 @@ async fn resume_includes_initial_messages_and_sends_prior_items() {
text: "resumed user message".to_string(),
}],
end_turn: None,
phase: None,
};
let prior_user_json = serde_json::to_value(&prior_user).unwrap();
writeln!(
@@ -218,6 +220,7 @@ async fn resume_includes_initial_messages_and_sends_prior_items() {
text: "resumed system instruction".to_string(),
}],
end_turn: None,
phase: None,
};
let prior_system_json = serde_json::to_value(&prior_system).unwrap();
writeln!(
@@ -239,6 +242,7 @@ async fn resume_includes_initial_messages_and_sends_prior_items() {
text: "resumed assistant message".to_string(),
}],
end_turn: None,
phase: Some(MessagePhase::Commentary),
};
let prior_item_json = serde_json::to_value(&prior_item).unwrap();
writeln!(
@@ -318,6 +322,25 @@ async fn resume_includes_initial_messages_and_sends_prior_items() {
.iter()
.position(|(role, text)| role == "assistant" && text == "resumed assistant message")
.expect("prior assistant message");
let prior_assistant = input
.iter()
.find(|item| {
item.get("role").and_then(|role| role.as_str()) == Some("assistant")
&& item
.get("content")
.and_then(|content| content.as_array())
.and_then(|content| content.first())
.and_then(|entry| entry.get("text"))
.and_then(|text| text.as_str())
== Some("resumed assistant message")
})
.expect("resumed assistant message request item");
assert_eq!(
prior_assistant
.get("phase")
.and_then(|phase| phase.as_str()),
Some("commentary")
);
let pos_permissions = messages
.iter()
.position(|(role, text)| role == "developer" && text.contains("<permissions instructions>"))
@@ -1210,6 +1233,7 @@ async fn azure_responses_request_includes_store_and_reasoning_ids() {
text: "message".into(),
}],
end_turn: None,
phase: None,
});
prompt.input.push(ResponseItem::WebSearchCall {
id: Some("web-search-id".into()),

View File

@@ -211,6 +211,7 @@ fn message_item(text: &str) -> ResponseItem {
role: "user".into(),
content: vec![ContentItem::InputText { text: text.into() }],
end_turn: None,
phase: None,
}
}

View File

@@ -1452,6 +1452,7 @@ async fn auto_compact_runs_after_resume_when_token_usage_is_over_limit() {
text: remote_summary.to_string(),
}],
end_turn: None,
phase: None,
},
codex_protocol::models::ResponseItem::Compaction {
encrypted_content: "ENCRYPTED_COMPACTION_SUMMARY".to_string(),
@@ -2275,6 +2276,7 @@ async fn auto_compact_counts_encrypted_reasoning_before_last_user() {
text: "REMOTE_COMPACT_SUMMARY".to_string(),
}],
end_turn: None,
phase: None,
},
codex_protocol::models::ResponseItem::Compaction {
encrypted_content: "ENCRYPTED_COMPACTION_SUMMARY".to_string(),
@@ -2395,6 +2397,7 @@ async fn auto_compact_runs_when_reasoning_header_clears_between_turns() {
text: "REMOTE_COMPACT_SUMMARY".to_string(),
}],
end_turn: None,
phase: None,
},
codex_protocol::models::ResponseItem::Compaction {
encrypted_content: "ENCRYPTED_COMPACTION_SUMMARY".to_string(),

View File

@@ -62,6 +62,7 @@ async fn remote_compact_replaces_history_for_followups() -> Result<()> {
text: "REMOTE_COMPACTED_SUMMARY".to_string(),
}],
end_turn: None,
phase: None,
},
ResponseItem::Compaction {
encrypted_content: "ENCRYPTED_COMPACTION_SUMMARY".to_string(),
@@ -184,6 +185,7 @@ async fn remote_compact_runs_automatically() -> Result<()> {
text: "REMOTE_COMPACTED_SUMMARY".to_string(),
}],
end_turn: None,
phase: None,
},
ResponseItem::Compaction {
encrypted_content: "ENCRYPTED_COMPACTION_SUMMARY".to_string(),
@@ -251,6 +253,7 @@ async fn remote_manual_compact_emits_context_compaction_items() -> Result<()> {
text: "REMOTE_COMPACTED_SUMMARY".to_string(),
}],
end_turn: None,
phase: None,
},
ResponseItem::Compaction {
encrypted_content: "ENCRYPTED_COMPACTION_SUMMARY".to_string(),
@@ -352,6 +355,7 @@ async fn remote_compact_persists_replacement_history_in_rollout() -> Result<()>
text: "COMPACTED_USER_SUMMARY".to_string(),
}],
end_turn: None,
phase: None,
},
ResponseItem::Compaction {
encrypted_content: "ENCRYPTED_COMPACTION_SUMMARY".to_string(),
@@ -363,6 +367,7 @@ async fn remote_compact_persists_replacement_history_in_rollout() -> Result<()>
text: "COMPACTED_ASSISTANT_NOTE".to_string(),
}],
end_turn: None,
phase: None,
},
];
let compact_mock = responses::mount_compact_json_once(

View File

@@ -158,6 +158,7 @@ async fn copy_paste_local_image_persists_rollout_request_shape() -> anyhow::Resu
},
],
end_turn: None,
phase: None,
};
assert_eq!(actual, expected);
@@ -239,6 +240,7 @@ async fn drag_drop_image_persists_rollout_request_shape() -> anyhow::Result<()>
},
],
end_turn: None,
phase: None,
};
assert_eq!(actual, expected);

View File

@@ -530,6 +530,7 @@ async fn review_input_isolated_from_parent_history() {
text: "parent: earlier user message".to_string(),
}],
end_turn: None,
phase: None,
};
let user_json = serde_json::to_value(&user).unwrap();
let user_line = serde_json::json!({
@@ -549,6 +550,7 @@ async fn review_input_isolated_from_parent_history() {
text: "parent: assistant reply".to_string(),
}],
end_turn: None,
phase: None,
};
let assistant_json = serde_json::to_value(&assistant).unwrap();
let assistant_line = serde_json::json!({