Skip to content

fix(ag-ui):subagent event forward#1870

Open
rudy2steiner wants to merge 2 commits into
agentscope-ai:mainfrom
rudy2steiner:forward_subagent_event
Open

fix(ag-ui):subagent event forward#1870
rudy2steiner wants to merge 2 commits into
agentscope-ai:mainfrom
rudy2steiner:forward_subagent_event

Conversation

@rudy2steiner

Copy link
Copy Markdown

Background

Fixes #1046 When a supervisor agent delegates to a sub-agent via SubAgentTool with SubAgentConfig.forwardEvents(true), the sub-agent's intermediate
reasoning/text was completely invisible to the AG-UI frontend — the UI saw TOOL_CALL_START, then a long pause, then TOOL_CALL_END + TOOL_CALL_RESULT arriving together. Effectively a "black box" during sub-agent execution,
especially painful for multi-step researchers/analysts.

Changes

  • added a new branch for non-last TOOL_RESULT events for subagent events
  • new unit test that constructs the same wire format SubAgentTool.forwardEvent produces, runs it through the adapter

@rudy2steiner rudy2steiner requested a review from a team June 22, 2026 14:29
@CLAassistant

CLAassistant commented Jun 22, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@codecov

codecov Bot commented Jun 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 33.33333% with 10 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...io/agentscope/core/tool/subagent/SubAgentTool.java 0.00% 6 Missing ⚠️
...agentscope/core/agui/adapter/AguiAgentAdapter.java 55.55% 0 Missing and 4 partials ⚠️

📢 Thoughts on this report? Let us know!

@AgentScopeJavaBot AgentScopeJavaBot added bug Something isn't working area/ext/integration External protocols & middleware integrations labels Jun 23, 2026

@AgentScopeJavaBot AgentScopeJavaBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 AI Review

This PR fixes the AG-UI adapter to correctly forward sub-agent intermediate events (reasoning/text) to the frontend when SubAgentTool has forwardEvents=true. The fix adds a new branch for non-last TOOL_RESULT events, extracting the inner Event from metadata and recursively converting it. Key concerns: (1) subagent_event magic string is a cross-module contract with no shared constant; (2) recursive convertEvent shares EventConversionState, risking currentTextMessageId state pollution; (3) no null-check on the inner Event extracted from metadata.

if (block instanceof ToolResultBlock toolResult) {
Map<String, Object> metadata = toolResult.getMetadata();
if (metadata != null) {
Object inner = metadata.get("subagent_event");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[major] subagent_event is a cross-module magic string shared between SubAgentTool (core) and this adapter (extensions). A typo here silently drops all sub-agent events. Extract to a shared constant, e.g. SubAgentTool.METADATA_KEY_SUBAGENT_EVENT, and add a debug log when the metadata value is not an Event instance.

if (block instanceof ToolResultBlock toolResult) {
Map<String, Object> metadata = toolResult.getMetadata();
if (metadata != null) {
Object inner = metadata.get("subagent_event");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[major] Recursive convertEvent(innerEvent, state) shares the same EventConversionState. If the sub-agent stream terminates without a final isLast=true REASONING event (e.g. cancellation, error), currentTextMessageId remains stuck on the sub-agent message ID, potentially corrupting subsequent supervisor text messages. Consider saving/restoring currentTextMessageId around the recursive call.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't fix. finishRun() already emits trailing TextMessageEnd for any unended message

if (inner instanceof Event innerEvent) {
events.addAll(convertEvent(innerEvent, state));
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[minor] No null/instance check on the value extracted from metadata. If the metadata contains a non-Event object, the recursive convertEvent call will fail with a ClassCastException. Add a defensive instanceof check before the recursive call.

@rudy2steiner rudy2steiner Jun 23, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't fix. inner instanceof Event innerEvent already does both null- and type-checking via pattern matching.won't fix

@itxaiohanglover

Copy link
Copy Markdown
Contributor

Good refactor! Extracting metadata keys as public constants makes them usable by downstream consumers without string duplication. The AG-UI adapter integration looks clean — forwarding sub-agent events through the metadata channel is a pragmatic approach.

@rudy2steiner rudy2steiner force-pushed the forward_subagent_event branch from 63f30de to 66497cb Compare June 26, 2026 13:27
@rudy2steiner rudy2steiner force-pushed the forward_subagent_event branch from 66497cb to 280ffcf Compare June 26, 2026 13:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/ext/integration External protocols & middleware integrations bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Sub-agent events are not visible in AG-UI frontend when using SubAgentTool with forwardEvents(true)

4 participants