feat: use OAI Responses API MessagePhase type directly in App Server v2 (#12422)

https://github.com/openai/codex/pull/10455 introduced the `phase` field,
and then https://github.com/openai/codex/pull/12072 introduced a
`MessagePhase` type in `v2.rs` that paralleled the `MessagePhase` type
in `codex-rs/protocol/src/models.rs`.

The app server protocol prefers `camelCase` while the Responses API uses
`snake_case`, so this meant we had two versions of `MessagePhase` with
different serialization rules. When the app server protocol refers to
types from the Responses API, we use the wire format of the the
Responses API even though it is inconsistent with the app server API.

This PR deletes `MessagePhase` from `v2.rs` and consolidates on the
Responses API version to eliminate confusion.
This commit is contained in:
Michael Bolin
2026-02-20 20:43:36 -08:00
committed by GitHub
parent a73efab8dd
commit 48af93399e
20 changed files with 251 additions and 117 deletions

View File

@@ -16,7 +16,7 @@ use crate::protocol::v2::TurnError;
use crate::protocol::v2::TurnStatus;
use crate::protocol::v2::UserInput;
use crate::protocol::v2::WebSearchAction;
use codex_protocol::models::MessagePhase as CoreMessagePhase;
use codex_protocol::models::MessagePhase;
use codex_protocol::protocol::AgentReasoningEvent;
use codex_protocol::protocol::AgentReasoningRawContentEvent;
use codex_protocol::protocol::AgentStatus;
@@ -190,17 +190,15 @@ impl ThreadHistoryBuilder {
self.current_turn = Some(turn);
}
fn handle_agent_message(&mut self, text: String, phase: Option<CoreMessagePhase>) {
fn handle_agent_message(&mut self, text: String, phase: Option<MessagePhase>) {
if text.is_empty() {
return;
}
let id = self.next_item_id();
self.ensure_turn().items.push(ThreadItem::AgentMessage {
id,
text,
phase: phase.map(Into::into),
});
self.ensure_turn()
.items
.push(ThreadItem::AgentMessage { id, text, phase });
}
fn handle_agent_reasoning(&mut self, payload: &AgentReasoningEvent) {
@@ -1196,7 +1194,7 @@ mod tests {
ThreadItem::AgentMessage {
id: "item-1".into(),
text: "Final reply".into(),
phase: Some(crate::protocol::v2::MessagePhase::FinalAnswer),
phase: Some(MessagePhase::FinalAnswer),
}
);
}