One task, request by request
One streamed run of test-runner shows every piece of the protocol in order, from the card read to the moment the server closes the stream.
Your planner wants the unit tests of payments-api run at commit 9f3c2e1, and it wants to watch the log grow while they run. It already holds the card of test-runner, whose capabilities include "streaming": true, so it sends the request with SendStreamingMessage.
This section follows that run frame by frame, from the streaming capture, and figure 1.3 shows it. When you finish this section, you can read any task stream and say what each frame changed in the stored task.
Before the first frame
Unless the card sets capabilities.streaming to true, the agent must refuse SendStreamingMessage with UnsupportedOperationError, as discovery explains. The request itself is short:
Host: localhost:41241
Content-Type: application/json
A2A-Version: 1.0
Accept: text/event-stream
…
"method": "SendStreamingMessage",
"params": {
"message": {
"messageId": "5bc8fbbc-bde5-4099-8164-d8399f767c45",
"role": "ROLE_USER",
"parts": [
{
"text": "Run the unit tests for payments-api at commit 9f3c2e1"
}
]
…The client minted the messageId itself, and it sent no taskId and no contextId, so the agent creates both. The response head is HTTP/1.1 200 OK with Content-Type: text/event-stream. Each event after it is one data: line that holds a complete JSON-RPC response, with the request's id of 1.
Frame 1: the task appears
A task stream has a fixed opening: the stream must begin with the Task object. This one is already in TASK_STATE_SUBMITTED:
"task": {
"id": "728ba084-97eb-422b-b94b-b0fe9153ce2c",
"contextId": "c026046c-6890-4830-9618-8fffa35cb080",
"status": {
"state": "TASK_STATE_SUBMITTED",
"timestamp": "2026-10-06T09:00:01.750Z"
},
"history": [
{
"messageId": "5bc8fbbc-bde5-4099-8164-d8399f767c45",
"contextId": "c026046c-6890-4830-9618-8fffa35cb080",
"taskId": "728ba084-97eb-422b-b94b-b0fe9153ce2c",
"role": "ROLE_USER",
…The server minted task 728ba084 and context c026046c, and it wrote both ids into its copy of the planner's message. From here on, the planner matches every frame to those two ids. The kit fills the optional timestamp of this first status, and the SDK leaves it out, as the kit README lists.
Frame 2: the work starts
Frame 2 is a statusUpdate that moves the task to TASK_STATE_WORKING, with the status message "Checking out 9f3c2e1 and starting the suite". Every status event names its task and its context, because taskId, contextId, and status are all REQUIRED.
Frames 3 to 8: the results arrive in pieces
The next six frames are artifactUpdate events, a type with no timestamp field. Frames 3 to 7 build one artifact, test-log, one line of the log at a time:
- Frame 3 carries the
artifactIdtest-log, thenametest-log.txt, and the first part. It has noappend, so it starts the artifact. - Frames 4, 5, and 6 repeat the
artifactId, leave out thename, and setappend: true. - Frame 7 sets both
append: trueandlastChunk: true, so the log is complete. - Frame 8 starts a second artifact,
summary, in one chunk: a data part with the media typeapplication/json, andlastChunk: true.
A lastChunk closes one artifact and nothing more: frame 8 carries its own while the task keeps running. Artifact and chunks shows how a client joins the chunks, and what the specification leaves open.
Frame 9 and the close
Frame 9 is the last statusUpdate. It moves the task to TASK_STATE_COMPLETED at 09:00:02.250Z, with the status message "1 failed, 11 passed". The task completed although a test failed, because the failure is part of the result.
Then the server closes the connection, and no frame announces it, because version 1.0 removed the final flag. The rule is that the stream must close when the task reaches a terminal state. At an interrupted state, the specification contradicts itself about whether a stream closes (conflict D3). Interrupted states sets out when a stream closes, and its figure 4.4 follows a stream that stays open.
The task after the stream
After the close, the task remains in the store, and a client reads it back with GetTask. That operation reads the final state of a task after a push notification or after a stream has ended. GetTask and ListTasks covers what it returns.
A2A protocol 1.0.1 · 3303592 · 2026-05-28 · Edition 2026.10