Summary
CodexAgent.run_stream() emits PartDeltaEvent for text chunks but never emits PartStartEvent or PartEndEvent. This breaks AG-UI text streaming because AGUIEventStream (from pydantic_ai.ui.ag_ui) requires the full event triplet to produce TEXT_MESSAGE_START / TEXT_MESSAGE_CONTENT / TEXT_MESSAGE_END.
Without TEXT_MESSAGE_START, the @assistant-ui/react-ag-ui runtime drops all orphaned TEXT_MESSAGE_CONTENT events — so no text ever appears in the browser.
Root Cause
In agentpool/agents/codex_agent/codex_converters.py, convert_codex_stream() yields:
yield PartDeltaEvent(delta=TextPartDelta(content_delta=chunk))
…but never yields a corresponding PartStartEvent(part=TextPart(...)) before the first delta, nor a PartEndEvent after the last delta.
AGUIEventStream state machine (from pydantic_ai):
PartStartEvent → TEXT_MESSAGE_START ✅ (never emitted)
PartDeltaEvent → TEXT_MESSAGE_CONTENT ✅ (emitted but dropped by client)
PartEndEvent → TEXT_MESSAGE_END ✅ (never emitted)
Reproduction
import asyncio
from agentpool.agents.codex_agent.codex_agent import CodexAgent
from agentpool.models.codex_agents import CodexAgentConfig
config = CodexAgentConfig(approval_policy="never")
agent = CodexAgent.from_config(config)
async def main():
async for event in agent.run_stream("Reply with exactly: PONG", store_history=False):
print(type(event).__name__, event)
asyncio.run(main())
Observed output (event types only):
RunStartedEvent
PartDeltaEvent (content_delta='P')
PartDeltaEvent (content_delta='ONG')
StreamCompleteEvent
Expected output (for AG-UI to work):
RunStartedEvent
PartStartEvent (part=TextPart(content=''))
PartDeltaEvent (content_delta='P')
PartDeltaEvent (content_delta='ONG')
PartEndEvent (part=TextPart(content='PONG'))
StreamCompleteEvent
Suggested Fix
In convert_codex_stream(), track whether the first text chunk has been seen and wrap deltas with the required start/end events:
text_started = False
accumulated = ""
async for chunk in codex_stream:
if is_text_chunk(chunk):
if not text_started:
yield PartStartEvent(index=0, part=TextPart(content=""))
text_started = True
accumulated += chunk.text
yield PartDeltaEvent(delta=TextPartDelta(content_delta=chunk.text))
if text_started:
yield PartEndEvent(index=0, part=TextPart(content=accumulated))
Environment
agentpool v2.9.14
pydantic_ai (whichever version agentpool ships with)
@assistant-ui/react-ag-ui latest
@ag-ui/client 0.0.47
Impact
AG-UI text streaming is completely broken for CodexAgent — no text reaches the browser frontend regardless of model response.
Summary
CodexAgent.run_stream()emitsPartDeltaEventfor text chunks but never emitsPartStartEventorPartEndEvent. This breaks AG-UI text streaming becauseAGUIEventStream(frompydantic_ai.ui.ag_ui) requires the full event triplet to produceTEXT_MESSAGE_START/TEXT_MESSAGE_CONTENT/TEXT_MESSAGE_END.Without
TEXT_MESSAGE_START, the@assistant-ui/react-ag-uiruntime drops all orphanedTEXT_MESSAGE_CONTENTevents — so no text ever appears in the browser.Root Cause
In
agentpool/agents/codex_agent/codex_converters.py,convert_codex_stream()yields:…but never yields a corresponding
PartStartEvent(part=TextPart(...))before the first delta, nor aPartEndEventafter the last delta.AGUIEventStreamstate machine (frompydantic_ai):Reproduction
Observed output (event types only):
Expected output (for AG-UI to work):
Suggested Fix
In
convert_codex_stream(), track whether the first text chunk has been seen and wrap deltas with the required start/end events:Environment
agentpoolv2.9.14pydantic_ai(whichever version agentpool ships with)@assistant-ui/react-ag-uilatest@ag-ui/client0.0.47Impact
AG-UI text streaming is completely broken for
CodexAgent— no text reaches the browser frontend regardless of model response.