Life of a Task
Life of a Task
In the Agent2Agent (A2A) protocol, interactions can range from simple, stateless exchanges to complex, long-running processes. When an agent receives a message from a client, it can respond in two fundamental ways:
- Stateless
Message: immediate, self-contained interactions that don’t require tracking state. - Stateful
Task: long-running or multi-step work tracked through a lifecycle, potentially requiring additional input and producing artifacts over time.
Grouping related interactions with contextId
contextId is a server-generated identifier that groups related Task and Message objects:
- On a first interaction, the server returns a new
contextId(and ataskIdif a task is created). - Subsequent client messages can include the same
contextIdto continue the session. - Clients may also include a
taskIdto continue that specific task.
This enables multi-turn collaboration and helps both client and server tie messages and tasks to a shared goal.
Message vs Task: when to choose which
The choice depends on the work:
- Message for trivial/transactional interactions: negotiation, clarification, quick answers, or lightweight operations.
- Task for stateful interactions: any work that benefits from tracking status, supporting retries/resubscribe, or producing artifacts.
Some agents always return messages, some always return tasks (even for simple answers), and others are hybrid: they use messages for negotiation, then create tasks once the scope is agreed.
Task refinements and follow-ups
Clients often refine results based on a previous task. This is modeled by starting another interaction under the same contextId, optionally referencing earlier tasks using referenceTaskIds. The server may respond with a new Task (common) or a Message if it can complete immediately.
Task immutability
Once a task reaches a terminal state (completed, canceled, rejected, failed), it does not restart. Follow-ups create new tasks within the same contextId. This gives:
- clear units of work,
- reliable traceability,
- predictable client orchestration.
Example follow-up scenario
- Client requests an artifact:
{
"jsonrpc": "2.0",
"id": "req-001",
"method": "message.send",
"params": {
"message": {
"role": "user",
"messageId": "msg-user-001",
"parts": [
{ "kind": "text", "text": "Generate an image of a sailboat on the ocean." }
]
}
}
}- Server responds with a completed task and an image artifact (illustrative):
{
"jsonrpc": "2.0",
"id": "req-001",
"result": {
"id": "task-boat-gen-123",
"contextId": "ctx-conversation-abc",
"status": { "state": "completed" },
"artifacts": [
{
"artifactId": "artifact-boat-v1-xyz",
"name": "sailboat_image.png",
"parts": [
{
"kind": "file",
"file": {
"name": "sailboat_image.png",
"mimeType": "image/png",
"bytes": "base64_encoded_png_data"
}
}
]
}
],
"kind": "task"
}
}- Client asks for a refinement under the same
contextIdand references the previous task:
{
"jsonrpc": "2.0",
"id": "req-002",
"method": "message.send",
"params": {
"message": {
"role": "user",
"messageId": "msg-user-002",
"contextId": "ctx-conversation-abc",
"referenceTaskIds": ["task-boat-gen-123"],
"parts": [
{ "kind": "text", "text": "Please modify the sailboat to be red." }
]
}
}
}The server typically creates a new task (same context, new task id), producing a new artifact version.