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.
- Message
- submitted
- working
- artifact
- 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 updateworking- Active; Shows progress, may cancelinput_required- Interrupted; Shows the question and sends a follow-upauth_required- Interrupted; Passes the allowed auth flowcompleted- Terminal; Validates artifactsfailed- Terminal; Classifies retry/escalationcanceled- Terminal; Expects no further updatesrejected- 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.
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.
A2A lifecycle simulator
No events published yet.
An artifact may update in parts. With streaming, the server publishes artifact updates withappend/lastChunksemantics. The client assembles them byartifactIdand 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.
An A2A Task is in input_required. What class is it, and what does the client do?