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 a taskId if a task is created).
  • Subsequent client messages can include the same contextId to continue the session.
  • Clients may also include a taskId to 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

  1. 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." }
      ]
    }
  }
}
  1. 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"
  }
}
  1. Client asks for a refinement under the same contextId and 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.