Skip to content

[Bug]:AguiAgentAdapter uses same messageId for reasoning and text events, causing text output to be lost in CopilotKit clients #1629

Description

@liuee123

Describe the bug

AguiAgentAdapter uses the same msg.getId() for both ThinkingBlock (reasoning) and TextBlock (text) events when a message contains both types of
blocks. This causes REASONING_MESSAGE_* and TEXT_MESSAGE_* events to share the same messageId, which breaks downstream AG-UI clients (CopilotKit) that use messageId as the primary key to distinguish and manage messages.

To Reproduce


  1. Configure AguiAgentAdapter with enableReasoning(true) and connect to an agent that produces both reasoning (thinking) and text output in a
    single response.
  2. Backend code:
    
    AguiAdapterConfig config = AguiAdapterConfig.builder()
    .enableReasoning(true)
    .build();
    AguiAgentAdapter adapter = new AguiAgentAdapter(agent, config);
    return adapter.run(input);
    
  3. Observe the SSE event stream — REASONING_MESSAGE_START, REASONING_MESSAGE_CONTENT, REASONING_MESSAGE_END and TEXT_MESSAGE_START,
    TEXT_MESSAGE_CONTENT, TEXT_MESSAGE_END all share the same messageId.
  4. Connect a CopilotKit frontend via HttpAgent. The text response does not render in the chat UI.
    
    Expected behavior
    
    Reasoning and text messages should have distinct messageId values so that AG-UI clients can treat them as separate messages. The text message
    should appear as a visible chat bubble, and the reasoning message should be handled independently (e.g., shown in a collapsible "thinking"
    section).
    
    Root cause
    
    In the adapter, both ThinkingBlock and TextBlock events derive their messageId from the same msg.getId(). Since downstream clients (CopilotKit in
    particular) identify messages by messageId, the shared ID causes the text content to be absorbed into the reasoning message. CopilotKit's
    AG-UI→GraphQL converter then drops all role: "reasoning" messages (by design, since reasoning is surfaced via subscriber callbacks separately),
    so the text content is lost entirely.
    
    Workaround (user side)
    
    Suffix text message IDs to differentiate them in the SSE stream before returning:
    
    return adapter.run(input)
    .map(event -> {
    if (event instanceof TextMessageStart msg) {
    return new TextMessageStart(msg.threadId(), msg.runId(), msg.messageId() + "-text", msg.role());
    } else if (event instanceof TextMessageContent msg) {
    return new TextMessageContent(msg.threadId(), msg.runId(), msg.messageId() + "-text", msg.delta());
    } else if (event instanceof TextMessageEnd msg) {
    return new TextMessageEnd(msg.threadId(), msg.runId(), msg.messageId() + "-text");
    }
    return event;
    });
    
    Environment
    
  • AgentScope-Java Version: 2.0.0-RC1
  • Java Version: 21
  • OS: Windows 11
  • AG-UI client: @ag-ui/client v0.0.55, CopilotKit v1.59.2
    

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions