Summary
@agentclientprotocol/codex-acp currently streams Codex agentMessage items with phase: "commentary" as normal assistant body text (agent_message_chunk) instead of thought/progress text (agent_thought_chunk).
This causes ACP clients that render thoughts separately to display Codex commentary/progress in the main assistant message body.
Expected Behavior
Codex agent messages should be mapped by phase:
phase: "commentary" -> agent_thought_chunk
phase: "final_answer" -> agent_message_chunk
phase: null / unknown -> agent_message_chunk
Actual Behavior
Both commentary and final answer text deltas are emitted as:
{
"sessionUpdate": "agent_message_chunk",
"content": {
"type": "text",
"text": "I will inspect the workspace."
}
}
Root Cause
Codex provides the phase on item/started:
{
"method": "item/started",
"params": {
"item": {
"type": "agentMessage",
"id": "msg_...",
"text": "",
"phase": "commentary"
}
}
}
But subsequent text deltas only contain itemId and delta:
{
"method": "item/agentMessage/delta",
"params": {
"itemId": "msg_...",
"delta": "I"
}
}
The current implementation handles item/agentMessage/delta without remembering the phase from item/started, so all deltas become agent_message_chunk.
Impact
ACP clients cannot distinguish Codex commentary/progress from final assistant text.
For example, in our WeCom integration, Codex progress text such as:
I will inspect the workspace.
is rendered in the main card body instead of the card's thinking/progress section.
Suggested Fix
Track the agentMessage phase by item.id when receiving item/started, then use that phase when handling item/agentMessage/delta.
private readonly activeAgentMessagePhases = new Map<string, MessagePhase | null>();
// item/started
if (event.item.type === "agentMessage") {
this.activeAgentMessagePhases.set(event.item.id, event.item.phase);
return null;
}
// item/agentMessage/delta
const phase = this.activeAgentMessagePhases.get(event.itemId);
return {
sessionUpdate:
phase === "commentary" ? "agent_thought_chunk" : "agent_message_chunk",
content: {
type: "text",
text: event.delta,
},
};
// item/completed
if (event.item.type === "agentMessage") {
this.activeAgentMessagePhases.delete(event.item.id);
return null;
}
History replay should also respect item.phase:
case "agentMessage":
return [{
sessionUpdate:
item.phase === "commentary" ? "agent_thought_chunk" : "agent_message_chunk",
content: {
type: "text",
text: item.text,
},
}];
Version
Observed in:
@agentclientprotocol/codex-acp@0.0.44
Summary
@agentclientprotocol/codex-acpcurrently streams CodexagentMessageitems withphase: "commentary"as normal assistant body text (agent_message_chunk) instead of thought/progress text (agent_thought_chunk).This causes ACP clients that render thoughts separately to display Codex commentary/progress in the main assistant message body.
Expected Behavior
Codex agent messages should be mapped by phase:
phase: "commentary"->agent_thought_chunkphase: "final_answer"->agent_message_chunkphase: null/ unknown ->agent_message_chunkActual Behavior
Both commentary and final answer text deltas are emitted as:
{ "sessionUpdate": "agent_message_chunk", "content": { "type": "text", "text": "I will inspect the workspace." } }Root Cause
Codex provides the
phaseonitem/started:{ "method": "item/started", "params": { "item": { "type": "agentMessage", "id": "msg_...", "text": "", "phase": "commentary" } } }But subsequent text deltas only contain
itemIdanddelta:{ "method": "item/agentMessage/delta", "params": { "itemId": "msg_...", "delta": "I" } }The current implementation handles
item/agentMessage/deltawithout remembering the phase fromitem/started, so all deltas becomeagent_message_chunk.Impact
ACP clients cannot distinguish Codex commentary/progress from final assistant text.
For example, in our WeCom integration, Codex progress text such as:
is rendered in the main card body instead of the card's thinking/progress section.
Suggested Fix
Track the
agentMessagephase byitem.idwhen receivingitem/started, then use that phase when handlingitem/agentMessage/delta.History replay should also respect
item.phase:Version
Observed in: