.NET: Fix A2A streaming artifact updates - #7722
Conversation
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d07945ad-4b76-48da-a499-a915c076cff9
There was a problem hiding this comment.
Pull request overview
Fixes A2A streaming by aggregating message streams and representing background output as task artifacts.
Changes:
- Aggregates message-only updates into one response.
- Adds ordered artifact chunking and task lifecycle states.
- Expands tests for IDs, contentless updates, failures, and cancellation.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
A2AAgentHandler.cs |
Routes streams to messages or task artifacts. |
ArtifactStreamWriter.cs |
Groups updates into correctly chunked artifacts. |
A2AAgentHandlerTests.cs |
Covers streaming and lifecycle scenarios. |
Suppressed comments (2)
dotnet/src/Microsoft.Agents.AI.Hosting.A2A/A2AAgentHandler.cs:340
- This checks whether any updates arrived, not whether the aggregated message has any convertible parts. A stream containing only role/finish/contentless updates has
updates.Count > 0, whileMessageConverter.ToPartsreturns an empty list, so this still enqueues the protocol-invalidparts: []message that the PR is intended to eliminate. Validate the aggregated message's parts before enqueueing and cover the message-only contentless case.
if (updates.Count == 0)
{
return;
}
dotnet/src/Microsoft.Agents.AI.Hosting.A2A/A2AAgentHandler.cs:307
ArtifactStreamWriteralways buffers the most recent content-bearing update, but it is completed only on the success path. If enumeration is canceled or throws, the terminal catch emits Canceled/Failed without flushing that buffer, so every interrupted stream drops its last yielded chunk (and a one-chunk failed stream emits no artifact at all). Ensure buffered output is finalized on terminal error paths while preserving delivery of the terminal status if artifact enqueueing also fails.
await artifactWriter.CompleteAsync(cancellationToken).ConfigureAwait(false);
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
MAF Automated Review — Iteration 1
Result: Findings reported
Scope: full PR (1 commit(s)): 99d325628cca
Model: gpt-5.6-sol
Overview
The review found 5 verified inline finding(s).
Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
5 verified findings remained after source verification (2 high, 3 medium) across 2 files. Details are attached to the affected lines below.
Affected areas: dotnet/src/Microsoft.Agents.AI.Hosting.A2A/A2AAgentHandler.cs, dotnet/src/Microsoft.Agents.AI.Hosting.A2A/ArtifactStreamWriter.cs
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d07945ad-4b76-48da-a499-a915c076cff9
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d07945ad-4b76-48da-a499-a915c076cff9
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d07945ad-4b76-48da-a499-a915c076cff9
Motivation & Context
A2A streaming currently creates a message for every agent update, including contentless updates with empty
parts. This violates A2A message-stream requirements, which require a message-only stream to contain exactly one message.The handler also lacks the alternative task-lifecycle stream for background-capable runs. Long-running responses should begin with a task and deliver progress and output through status and artifact updates until reaching a terminal state. Without these two distinct paths, strict A2A clients cannot reliably reconstruct either direct responses or streamed task output.
Description & Review Guide
append/lastChunkbehavior; handle repeated or missing message IDs; and emit terminal task states on completion, cancellation, or failure.ArtifactStreamWriterand task lifecycle mapping.Related Issue
Fixes #5868
Contribution Checklist
breaking changelabel (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.