You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Feature]: AG-UI stream does not expose which text is the final answer, nor the outcome of a tool call #3381
Context. We build product agents on HarnessAgent + the AG-UI endpoint (2.0.4-SNAPSHOT built from main). A multi-step run emits several TEXT_MESSAGE_* blocks (the model narrates between tool calls). Clients usually want to fold the intermediate narration and show only the final answer, and to know whether a tool call succeeded, failed or was denied.
What the client sees today.
Every model call finishes with GenerateReason.MODEL_STOP (observed in all cases except user interrupt), and the adapter does not forward any reason. The client cannot tell whether a text block is the final answer or an intermediate step until RUN_FINISHED arrives, and even then not which block it was.
TOOL_CALL_RESULT carries only the content; success / error / denied / interrupted (ToolResultState) is not exposed.
STEP_STARTED / STEP_FINISHED from the protocol are not emitted, so model-call boundaries are not visible either.
Workaround. We infer the final answer from event order (last text block of a normally finished run, even when it is followed by a pending confirmation or frontend tool call) and rebuild states from ToolResultEndEvent server-side. This only works inside the same process; a pure AG-UI client cannot do it.
Suggestion. Emit STEP_STARTED/FINISHED per model call and expose the finish reason (e.g. on the step or a custom event), and expose the tool result state on TOOL_CALL_RESULT (or an accompanying event). Happy to adapt our tracking once either lands.
The three claims in this report were checked against main (e9721285c) and all are confirmed:
Finish reason is not forwarded.Msg.getGenerateReason() falls back to MODEL_STOP whenever metadata is unset (agentscope-core/.../message/Msg.java:641-656), and TOOL_CALLS is never set anywhere in production code. In the AG-UI adapter GenerateReason is only used to gate TOOL_SUSPENDED interrupts (AgentLifecycleEventConverter.java:98); no converter forwards it, and RUN_FINISHED carries no reason field (AgentLifecycleEventConverter.java:85-91).
Tool result state is dropped.AguiEvent.ToolCallResult has no state field (AguiEvent.java:557-621). ToolResultEventConverter.java:46-51 only checks RUNNING (to suspend) and discards SUCCESS/ERROR/DENIED/INTERRUPTED; AguiStreamContext.endToolResult (284-300) emits with rawEvent=null. The state is available upstream (ToolResultEndEvent.getState()), so this is a plumbing gap, not a missing capability.
Scope:agentscope-extensions-agui (AguiStreamContext, ToolResultEventConverter, AgentLifecycleEventConverter, ModelCallUsageEventConverter, AguiEvent/AguiEvents, AguiAdapterConfig and their tests); optionally agentscope-agui-spring-boot-starter if the new behaviour is flag-exposed.
Compatibility: additive. New STEP_* events and an optional state field on TOOL_CALL_RESULT keep existing constructors/JSON shape, so clients that ignore unknown fields are unaffected. Strict-schema clients and UIs that render every event would see new traffic — gate emission behind an AguiAdapterConfig flag, following the existing emitTokenUsage / enableReasoning precedent.
One caveat on the "which text is the final answer" half: a meaningful per-step reason does not exist today. ModelCallEndEvent carries only token usage, so emitting STEP_STARTED/FINISHED per model call is enough for clients to derive "the final answer is the text of the last completed step", but surfacing an actual TOOL_CALLS vs MODEL_STOP reason per step requires a core change (ReActAgent / ModelCallEndEvent), which is a separate discussion.
Overlap: genuinely additive. #3312 touches the same emission point (AguiStreamContext.endToolResult) but is about the content; #3307 is the opposite direction (input tool messages, AguiMessageConverter.java:301 hardcodes SUCCESS); #1861 only added the event types without emitters.
Recommendation: proceed — ship STEP boundaries + tool result state as a contained, additive adapter change behind a config flag; treat the per-step generate-reason enrichment as a follow-up that needs a core-semantics decision.
I'd like to contribute to this. I noticed #3013 already addresses intermediate/terminal text classification, so I'd propose starting with opt-in STEP_STARTED / STEP_FINISHED events for model-call boundaries, with tests and documentation.
Is that scope available? If exposing tool-result states is also unclaimed, I'd be happy to include it after coordinating with #3324. Could you assign the agreed scope to me?
Activity
oss-maintainer commentedon Oct 1, 2026
Issue Evaluation
Category:
enhancement| Status: Evaluated | Feasibility: feasible (moderate, adapter-local)The three claims in this report were checked against
main(e9721285c) and all are confirmed:Msg.getGenerateReason()falls back toMODEL_STOPwhenever metadata is unset (agentscope-core/.../message/Msg.java:641-656), andTOOL_CALLSis never set anywhere in production code. In the AG-UI adapterGenerateReasonis only used to gateTOOL_SUSPENDEDinterrupts (AgentLifecycleEventConverter.java:98); no converter forwards it, andRUN_FINISHEDcarries no reason field (AgentLifecycleEventConverter.java:85-91).AguiEvent.ToolCallResulthas no state field (AguiEvent.java:557-621).ToolResultEventConverter.java:46-51only checksRUNNING(to suspend) and discardsSUCCESS/ERROR/DENIED/INTERRUPTED;AguiStreamContext.endToolResult(284-300) emits withrawEvent=null. The state is available upstream (ToolResultEndEvent.getState()), so this is a plumbing gap, not a missing capability.StepStarted/StepFinishedexist as types (AguiEvent.java:1179, 1221;AguiEventType.java:89-90, added by [Feature]: align AguiEvent with the AG-UI protocol spec - add missing event types #1861) but nothing ever emits them;ModelCallStartEventis registered and then ignored (AgentLifecycleEventConverter.java:56-93).Scope:
agentscope-extensions-agui(AguiStreamContext,ToolResultEventConverter,AgentLifecycleEventConverter,ModelCallUsageEventConverter,AguiEvent/AguiEvents,AguiAdapterConfigand their tests); optionallyagentscope-agui-spring-boot-starterif the new behaviour is flag-exposed.Compatibility: additive. New
STEP_*events and an optional state field onTOOL_CALL_RESULTkeep existing constructors/JSON shape, so clients that ignore unknown fields are unaffected. Strict-schema clients and UIs that render every event would see new traffic — gate emission behind anAguiAdapterConfigflag, following the existingemitTokenUsage/enableReasoningprecedent.One caveat on the "which text is the final answer" half: a meaningful per-step reason does not exist today.
ModelCallEndEventcarries only token usage, so emittingSTEP_STARTED/FINISHEDper model call is enough for clients to derive "the final answer is the text of the last completed step", but surfacing an actualTOOL_CALLSvsMODEL_STOPreason per step requires a core change (ReActAgent /ModelCallEndEvent), which is a separate discussion.Overlap: genuinely additive. #3312 touches the same emission point (
AguiStreamContext.endToolResult) but is about the content; #3307 is the opposite direction (input tool messages,AguiMessageConverter.java:301hardcodesSUCCESS); #1861 only added the event types without emitters.Recommendation: proceed — ship STEP boundaries + tool result state as a contained, additive adapter change behind a config flag; treat the per-step generate-reason enrichment as a follow-up that needs a core-semantics decision.
Automated evaluation by github-manager-bot
wyi184246-creator commentedon Oct 4, 2026
I'd like to contribute to this. I noticed #3013 already addresses intermediate/terminal text classification, so I'd propose starting with opt-in STEP_STARTED / STEP_FINISHED events for model-call boundaries, with tests and documentation.
Is that scope available? If exposing tool-result states is also unclaimed, I'd be happy to include it after coordinating with #3324. Could you assign the agreed scope to me?