Chapter 24

Task and Artifact Form the Work Lifecycle

The Task is the heart of A2A. It holds an id, a context, a status, a history, and artifacts. Terminal states finish the work; interrupted states let it request input or authentication. And an Artifact is a materialized result, not just the last message: it, not the text in a chat, is what the task existed for.

The lifecycle itself is easiest to see as a chain of states.

  1. Message
  2. submitted
  3. working
  4. artifact
  5. completed

Behind this line is a table of states - each tells the client what to do next.

State · Class · What the client does

  • submitted - Active; Saves the taskId, waits for an update
  • working - Active; Shows progress, may cancel
  • input_required - Interrupted; Shows the question and sends a follow-up
  • auth_required - Interrupted; Passes the allowed auth flow
  • completed - Terminal; Validates artifacts
  • failed - Terminal; Classifies retry/escalation
  • canceled - Terminal; Expects no further updates
  • rejected - Terminal; Doesn't repeat without changing the request

The states fall into three classes, and this division determines the client's behavior. Active (submitted, working) - the client waits and shows progress. Interrupted (input_required, auth_required) - the client answers a question or passes auth. Terminal (completed, failed, canceled, rejected) - there will be no further updates, and the reaction to each terminal differs: validate artifacts, classify a retry, don't repeat without changing the request.

The artifact itself, in our example, carries two representations at once - Markdown for a human and JSON for code.

TypeScript
    try {
      const review = reviewRelease(reference.service, reference.version);
      const artifact: Artifact = {
        artifactId: randomUUID(),
        name: "release-review.md",
        description: "Deterministic release-readiness report",
        parts: [
          {
            content: { $case: "text", value: reviewToMarkdown(review) },
            metadata: undefined,
            filename: "release-review.md",
            mediaType: "text/markdown"
          },
          {
            content: { $case: "data", value: review },
            metadata: undefined,
            filename: "release-review.json",
            mediaType: "application/json"
          }
        ],
        metadata: { decision: review.decision },
        extensions: []
      };

      eventBus.publish(AgentEvent.artifactUpdate({
        taskId,
        contextId,
        artifact,
        append: false,
        lastChunk: true,
        metadata: undefined
      }));

Stepping through the states by hand and feeling the transitions is helped by the simulator.

Interactive lab 5

A2A lifecycle simulator

No events published yet.
An artifact may update in parts. With streaming, the server publishes artifact updates with append/lastChunk semantics. The client assembles them by artifactId and doesn't treat each chunk as a new result - otherwise one report turns into a dozen.

We know what an A2A agent's work is made of. Now let's build the agent itself - and the first architectural decision here is about what to separate from the protocol.

Knowledge check

An A2A Task is in input_required. What class is it, and what does the client do?

Links