Skip to content

CodexAgent missing PartStartEvent/PartEndEvent breaks AG-UI text streaming #28

@tbrandenburg

Description

@tbrandenburg

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions