Skip to main content

A2A Event Flow Architecture - Complete Analysis

Status: 🟢 In-use Category: Architecture & Core Design Date: October 27, 2025

Overview

This document provides a thorough analysis of the Agent-to-Agent (A2A) protocol event flow in the CAIPE platform, from end client through supervisor to sub-agents, documenting actual event types, streaming behavior, and data flow patterns.

Architecture Layers

End Client (curl/browser)
↓ HTTP POST with SSE
Platform Engineer Supervisor (:8000)
↓ HTTP POST A2A
Sub-Agent (e.g., ArgoCD :8001)
↓ MCP Client
MCP Server (ArgoCD tools)

Architecture Flow Diagram

Complete Event Flow Sequence

A2A Event Types

1. Task Events

Task Submission (kind: "task")

Direction: Supervisor → Client When: Immediately after request received Structure:

{
"id": "test-id",
"jsonrpc": "2.0",
"result": {
"kind": "task",
"id": "0b18d90c-b92a-40d1-a205-df9fc70f739c",
"status": {"state": "submitted"},
"contextId": "07c0f068-23ce-41e1-a989-f428769b5033",
"history": [{
"kind": "message",
"role": "user",
"parts": [{"kind": "text", "text": "show argocd version"}],
"messageId": "msg-supervisor-1"
}]
}
}

Characteristics:

  • ✅ Sent once per request
  • ✅ Contains full message history
  • ✅ Provides taskId for tracking

2. Artifact Update Events (kind: "artifact-update")

Execution Plan Streaming (name: "execution_plan_streaming")

Direction: Supervisor → Client When: LLM generates execution plan Streaming Type: Token-by-token streaming with append flags

First Chunk:

{
"result": {
"kind": "artifact-update",
"append": false,
"lastChunk": false,
"artifact": {
"artifactId": "20e09366-fc4d-4485-b1b4-b4651415d22c",
"name": "execution_plan_streaming",
"description": "Execution plan streaming in progress",
"parts": [{"kind": "text", "text": "⟦"}]
}
}
}

Subsequent Chunks:

{
"result": {
"kind": "artifact-update",
"append": true, // ← Reuses same artifactId
"lastChunk": false,
"artifact": {
"artifactId": "20e09366-fc4d-4485-b1b4-b4651415d22c", // ← Same ID
"name": "execution_plan_streaming",
"description": "Execution plan streaming in progress",
"parts": [{"kind": "text", "text": "**"}] // ← Next token
}
}
}

Characteristics:

  • ✅ Token-by-token streaming
  • ✅ Single shared artifactId across all chunks
  • append=false for first chunk, append=true for rest
  • ✅ Wrapped in ⟦...⟧ Unicode markers
  • ✅ Each chunk contains 1-10 characters

Execution Plan Complete (name: "execution_plan_update")

Direction: Supervisor → Client When: After execution plan streaming completes Streaming Type: Single complete chunk

{
"result": {
"kind": "artifact-update",
"append": true,
"lastChunk": false,
"artifact": {
"artifactId": "75d62509-51c0-4e04-b5be-2908036c4a8a", // ← NEW ID
"name": "execution_plan_update",
"description": "Complete execution plan streamed to user",
"parts": [{
"kind": "text",
"text": "⟦**🎯 Execution Plan...**⟧" // ← Full plan
}]
}
}
}

Characteristics:

  • ✅ Sent once after streaming completes
  • ⚠️ Currently uses different artifact ID (potential issue)
  • ✅ Contains complete plan text

Tool Notifications (name: "tool_notification_start", "tool_notification_complete")

Direction: Supervisor → Client When: Tool (sub-agent) invocation starts/ends Streaming Type: Single-event notifications

Start Notification:

{
"result": {
"kind": "artifact-update",
"append": false,
"artifact": {
"artifactId": "d8373de2-1d91-4484-a8ec-dce88b077bd6",
"name": "tool_notification_start",
"description": "Tool call started: argocd",
"parts": [{
"kind": "text",
"text": "🔧 Supervisor: Calling Agent Argocd...\n"
}]
}
}
}

Characteristics:

  • ✅ Each notification gets unique artifact ID
  • append=false (standalone notifications)
  • ✅ User-friendly emoji prefixes

Streaming Result (name: "streaming_result")

Direction: Supervisor → Client When: Forwarding sub-agent response tokens Streaming Type: Token-by-token streaming with append flags

First Chunk:

{
"result": {
"kind": "artifact-update",
"append": false,
"lastChunk": false,
"artifact": {
"artifactId": "04c6b73a-fb00-40c4-a23c-3da41a1334bd",
"name": "streaming_result",
"description": "Streaming result from Platform Engineer",
"parts": [{"kind": "text", "text": "Here"}]
}
}
}

Subsequent Chunks:

{
"result": {
"kind": "artifact-update",
"append": true,
"artifact": {
"artifactId": "04c6b73a-fb00-40c4-a23c-3da41a1334bd", // ← Same ID
"name": "streaming_result",
"parts": [{"kind": "text", "text": " is"}]
}
}
}

Characteristics:

  • ✅ Token-by-token streaming from sub-agent
  • ✅ Single shared artifactId
  • append=false for first, append=true for rest

Partial Result (name: "partial_result")

Direction: Supervisor → Client When: End of streaming sequence Streaming Type: Single complete result

{
"result": {
"kind": "artifact-update",
"append": false,
"lastChunk": true, // ← Marks end
"artifact": {
"artifactId": "3881cbbc-08e8-4c5b-90d5-a8dccf4368f7",
"name": "partial_result",
"description": "Partial result from Platform Engineer (stream ended)",
"parts": [{
"kind": "text",
"text": "Here is the ArgoCD version information..." // ← Complete text
}]
}
}
}

Characteristics:

  • lastChunk=true indicates end
  • ✅ Contains complete accumulated text
  • ✅ Sent once at completion

3. Status Update Events (kind: "status-update")

Sub-Agent Working Status

Direction: Sub-Agent → Supervisor When: During sub-agent processing Streaming Type: Individual token events

{
"result": {
"kind": "status-update",
"final": false,
"contextId": "57a8b9ea-1580-422f-a387-177c4840b133",
"taskId": "06c54ebb-50cd-4210-9a35-13899c23815e",
"status": {
"state": "working",
"message": {
"kind": "message",
"role": "agent",
"messageId": "cc07f0d2-ab2c-46ba-ab88-d55eebec96cf", // ← Unique per token
"contextId": "57a8b9ea-1580-422f-a387-177c4840b133",
"taskId": "06c54ebb-50cd-4210-9a35-13899c23815e",
"parts": [{"kind": "text", "text": "H"}] // ← Single token
}
}
}
}

Characteristics:

  • ✅ Each token has unique messageId
  • final=false for all intermediate tokens
  • state="working" during processing
  • ✅ Can contain tool notifications: "🔧 Calling tool: version_service__version"

Sub-Agent Final Status

Direction: Sub-Agent → Supervisor When: Sub-agent task complete Streaming Type: Single completion event

{
"result": {
"kind": "status-update",
"final": true, // ← Marks completion
"status": {
"state": "completed"
},
"taskId": "06c54ebb-50cd-4210-9a35-13899c23815e"
}
}

Characteristics:

  • final=true indicates end
  • state="completed" (or "failed")
  • ✅ Sent once at task completion

Event Flow Comparison

Supervisor Events (Platform Engineer → Client)

Event TypeArtifact NameStreamingappend FlagUse Case
artifact-updateexecution_plan_streamingToken-by-tokenfalse (first), true (rest)LLM execution plan
artifact-updateexecution_plan_updateSingle chunktrueComplete plan
artifact-updatetool_notification_startSingle chunkfalseTool call started
artifact-updatestreaming_resultToken-by-tokenfalse (first), true (rest)Sub-agent response
artifact-updatepartial_resultSingle chunkfalseFinal accumulated result

Sub-Agent Events (ArgoCD → Supervisor)

Event TypeStatus Statefinal FlagUse Case
status-updateworkingfalseEach response token
status-updateworkingfalseTool notifications
status-updatecompletedtrueTask completion
artifact-updatecurrent_resultN/AEmpty final artifact

Token Streaming vs. Chunks

Token-by-Token Streaming

Used for: Execution plans, sub-agent responses Mechanism:

  1. First event: append=false, new artifactId
  2. Subsequent events: append=true, same artifactId
  3. Each event contains 1-10 characters
  4. Frontend appends to same display area

Example:

Event 1: append=false, artifactId="abc", text="H"
Event 2: append=true, artifactId="abc", text="ere"
Event 3: append=true, artifactId="abc", text=" is"
Result: "Here is"

Single-Chunk Events

Used for: Tool notifications, completion markers Mechanism:

  1. One event with complete content
  2. append=false (standalone)
  3. Unique artifactId per notification
  4. Frontend displays as new element

Example:

Event: append=false, artifactId="xyz", text="🔧 Calling Agent..."
Result: New notification appears

Complete Example Flow

Query: "show argocd version"

Step 1: Client → Supervisor

curl -X POST http://10.99.255.178:8000 \
-H "Content-Type: application/json" \
-d '{"id":"req-1","method":"message/stream","params":{...}}'

Step 2: Supervisor Responds

Task Submission:

{"kind":"task","status":"submitted"}

Execution Plan Streaming (50+ events):

{"kind":"artifact-update","name":"execution_plan_streaming","append":false,"text":"⟦"}
{"kind":"artifact-update","name":"execution_plan_streaming","append":true,"text":"**"}
{"kind":"artifact-update","name":"execution_plan_streaming","append":true,"text":"🎯"}
... (token by token)
{"kind":"artifact-update","name":"execution_plan_streaming","append":true,"text":"⟧"}

Execution Plan Complete:

{"kind":"artifact-update","name":"execution_plan_update","text":"⟦**🎯 Execution Plan...**⟧"}

Tool Notification:

{"kind":"artifact-update","name":"tool_notification_start","text":"🔧 Supervisor: Calling Agent Argocd..."}

Step 3: Supervisor → Sub-Agent (Internal A2A)

POST http://agent-argocd-p2p:8000
{"id":"sub-req","method":"message/stream","params":{...}}

Step 4: Sub-Agent Responds (500+ events)

Working Status (each token):

{"kind":"status-update","state":"working","text":"H"}
{"kind":"status-update","state":"working","text":"ere"}
{"kind":"status-update","state":"working","text":" is"}
... (hundreds of tokens)
{"kind":"status-update","state":"working","text":"v3.1.8+becb020"}

Final Status:

{"kind":"artifact-update","name":"current_result","lastChunk":true,"text":""}
{"kind":"status-update","final":true,"state":"completed"}

Step 5: Supervisor Forwards to Client

Streaming Result (500+ events):

{"kind":"artifact-update","name":"streaming_result","append":false,"text":"Here"}
{"kind":"artifact-update","name":"streaming_result","append":true,"text":" is"}
... (forwarding each token)

Partial Result:

{"kind":"artifact-update","name":"partial_result","lastChunk":true,"text":"Here is the ArgoCD version..."}

Final Status:

{"kind":"status-update","final":true,"state":"completed"}

Key Insights

Streaming Efficiency

  • Supervisor: Streams LLM tokens directly to client (low latency)
  • Sub-Agent: Streams every single character (very granular)
  • Total Events: 600+ events for simple version query

Artifact ID Management

  • Execution Plan: Single ID shared across ~50 chunks
  • Tool Notifications: Unique ID per notification
  • Streaming Result: Single ID shared across ~500 chunks
  • ⚠️ Issue: execution_plan_update uses different ID than streaming chunks

Append Flag Pattern

First chunk:     append=false, artifactId="new-id"
Subsequent: append=true, artifactId="same-id"
Standalone: append=false, artifactId="unique-id"

Message IDs

  • Supervisor: Reuses artifactId with append flag
  • Sub-Agent: Unique messageId per token in status-update

Protocol Specifications

A2A Message Format

{
"id": "request-id",
"jsonrpc": "2.0",
"method": "message/stream",
"params": {
"message": {
"role": "user|agent",
"parts": [{"kind": "text", "text": "content"}],
"messageId": "unique-message-id"
}
}
}

A2A Response Format

{
"id": "request-id",
"jsonrpc": "2.0",
"result": {
"kind": "task|artifact-update|status-update",
"contextId": "context-uuid",
"taskId": "task-uuid",
... (kind-specific fields)
}
}

Frontend Integration

Event Handling

// Handle execution plan streaming
if (event.kind === "artifact-update" &&
event.artifact.name === "execution_plan_streaming") {
if (event.append === false) {
// Create new plan display
planElement = createPlanElement(event.artifact.artifactId);
} else {
// Append to existing plan
planElement.append(event.artifact.parts[0].text);
}
}

// Handle tool notifications
if (event.kind === "artifact-update" &&
event.artifact.name === "tool_notification_start") {
// Show new notification (don't append)
showNotification(event.artifact.parts[0].text);
}

// Handle streaming results
if (event.kind === "artifact-update" &&
event.artifact.name === "streaming_result") {
if (event.append === false) {
resultElement = createResultElement(event.artifact.artifactId);
} else {
resultElement.append(event.artifact.parts[0].text);
}
}

Testing Commands

Test Supervisor

curl -X POST http://10.99.255.178:8000 \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{"id":"test-1","method":"message/stream","params":{"message":{"role":"user","parts":[{"kind":"text","text":"show argocd version"}],"messageId":"msg-1"}}}'

Test Sub-Agent Direct

curl -X POST http://localhost:8001 \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{"id":"test-2","method":"message/stream","params":{"message":{"role":"user","parts":[{"kind":"text","text":"show argocd version"}],"messageId":"msg-2"}}}'

External References

Internal Documentation