Thomas Piacentini
← Workshop

Chapter

GetTask and ListTasks

Original text · English

GetTask and ListTasks are your recovery path, and both follow exact rules about which parts of a task they leave out.

The planner's stream to deployer drops. A webhook says a test run has finished. The planner restarts with nothing but task ids in its log. In each case the next step is the same: ask the agent for the task it has stored.

When you finish this section, you can fetch the history you need, page through an agent's tasks, and explain a missing field.

GetTask returns the stored task

GetTask takes the task id and an optional historyLength {{proto GetTaskRequest}}. It returns the Task as the server holds it: status, artifacts, and history.

An id you are not allowed to see gets TaskNotFoundError {{spec §3.1.3}}, so a not-found answer never proves that a task is gone.

historyLength

History: the messages a task has kept, apart from the current status message. The historyLength parameter sets how many a response carries, with the same meaning in every operation {{spec §3.2.4}}:

  • Unset: no limit. The server returns its default amount of history, which the implementation decides and which can be all of it {{spec §3.2.4}}.
  • A number above zero: at most that many recent messages from the history of the task {{spec §3.2.4}}.
  • Zero: no history, and the history field should be left out {{spec §3.2.4}}.

With historyLength 1, the kit returns only the newest message of the review task:

GetTask with historyLength 1jsonFull source
  "method": "GetTask",
  "params": {
    "id": "e5c95b09-6d4b-4b3b-8693-91e81472d12f",
    "historyLength": 1
  }
…
    "history": [
      {
        "messageId": "8f9ee658-e185-409a-9294-8d4d97ef77bb",
        …
        "role": "ROLE_AGENT",
        "parts": [
          {
            "text": "Comparing the diff against main"
          }
        ]
      }
    ],
The JSON-RPC envelopes, the reply's ids, status, and artifacts, and the ids inside the message are cut.
Original source
title: GetTask with historyLength 1
source: capture/out/13-history.http
lang: json
note: The JSON-RPC envelopes, the reply's ids, status, and artifacts, and the ids inside the message are cut.
---
  "method": "GetTask",
  "params": {
    "id": "e5c95b09-6d4b-4b3b-8693-91e81472d12f",
    "historyLength": 1
  }
…
    "history": [
      {
        "messageId": "8f9ee658-e185-409a-9294-8d4d97ef77bb",
        …
        "role": "ROLE_AGENT",
        "parts": [
          {
            "text": "Comparing the diff against main"
          }
        ]
      }
    ],

That message is not the newest thing the agent said. The final status message, "2 findings", stays in status.message. In the kit and the reference SDK, a status message joins history only when a newer one replaces it {{sdk src/a2a/server/tasks/task_manager.py}}. Read history and status.message together.

The server can also apply a lower limit {{proto GetTaskRequest}}, and the history does not have to keep every message {{spec §3.7}}. The unset case is implementation-defined, and the SDKs also read 0 and a negative value in different ways:

SDK historyLength unset 0 below 0
a2a-python 1.2.2 all history in both operations an empty history array InvalidParamsError {{sdk src/a2a/utils/task.py}}
a2a-go all history in GetTask, the last 100 messages in ListTasks the field is omitted the field is omitted {{sdk-go a2asrv/handler.go at v2.6.0}}
a2a-java all history in GetTask, none in ListTasks an empty list InvalidParamsError {{sdk-java requesthandlers/DefaultRequestHandler.java at v1.4.0.Final}}
a2a-js all history in both operations the field is omitted omitted over JSON-RPC, HTTP 400 over REST {{sdk-js src/server/request_handler/default_request_handler.ts at v1.3.0}}
a2a-dotnet all history in both operations an empty array in GetTask, no field in ListTasks InvalidParamsError over JSON-RPC {{sdk-dotnet src/A2A/Server/A2AServer.cs at v1.0.0-preview2}}
a2a-rs all history in both operations an empty history an empty history {{sdk-rust a2a-server/src/task_store/mod.rs at a2a-server-lf-v0.5.1}}

ListTasks returns pages, newest first

ListTasks filters by contextId, status, and statusTimestampAfter, and shapes the page with pageSize, pageToken, historyLength, and includeArtifacts {{proto ListTasksRequest}}. It returns only the tasks that the authenticated client can see {{spec §3.1.4}}, under four rules:

  • Size: without pageSize, a page holds at most 50 tasks {{proto ListTasksRequest}}, and the maximum is 100.
  • Order: tasks must come sorted by their status timestamp, newest update first {{spec §3.1.4}}.
  • Token: the response must always contain nextPageToken {{spec §3.1.4}}. On the last page it holds the empty string.
  • Artifacts: when includeArtifacts is false, which is the default, the artifacts field must be left out completely {{spec §3.1.4}}.

The kit asks test-runner, which holds five tasks, for pageSize 2 and historyLength 0:

the first page of ListTasksjsonFull source
    "tasks": [
      {
        "id": "f22ed7af-848d-4f4d-8cd1-3204d1e1bbaf",
        …
          "state": "TASK_STATE_CANCELED",
          …
          "timestamp": "2026-10-06T09:00:03.750Z"
      …
        "id": "8ae21dcb-7449-4d90-82a6-ac01b20fd782",
        …
          "state": "TASK_STATE_FAILED",
          …
          "timestamp": "2026-10-06T09:00:03.000Z"
      …
    ],
    "nextPageToken": "eyJ0cyI6IjIwMjYtMTAtMDZUMDk6MDA6MDMuMDAwWiIs…",
    "pageSize": 2,
    "totalSize": 5
Each task is cut to its id, state, and status timestamp, and the token is cut after its first 44 characters.
Original source
title: the first page of ListTasks
source: capture/out/12-list-tasks.http
lang: json
note: Each task is cut to its id, state, and status timestamp, and the token is cut after its first 44 characters.
---
    "tasks": [
      {
        "id": "f22ed7af-848d-4f4d-8cd1-3204d1e1bbaf",
        …
          "state": "TASK_STATE_CANCELED",
          …
          "timestamp": "2026-10-06T09:00:03.750Z"
      …
        "id": "8ae21dcb-7449-4d90-82a6-ac01b20fd782",
        …
          "state": "TASK_STATE_FAILED",
          …
          "timestamp": "2026-10-06T09:00:03.000Z"
      …
    ],
    "nextPageToken": "eyJ0cyI6IjIwMjYtMTAtMDZUMDk6MDA6MDMuMDAwWiIs…",
    "pageSize": 2,
    "totalSize": 5

The second page holds the completed runs 728ba084 and d0c28826. Neither page carries artifacts, although both completed runs stored a log and a summary. A third request filters on TASK_STATE_CANCELED and gets one task, a pageSize of 50, and an empty nextPageToken. Figure draws the pages beside the history regimes.

Fig. 4.5history regimes and list pagesstructure
`GetTask` trims history to the newest messages you ask for, and `ListTasks` returns newest-first pages that leave out artifacts until you ask for them.
GetTask trims history to the newest messages you ask for, and ListTasks returns newest-first pages that leave out artifacts until you ask for them. Top, the stored history of task e5c95b09 and what each historyLength returns. Bottom, the five tasks on test-runner in list order, grouped into the pages the kit requested. Ids are shortened to 8 characters. From capture/out/13-history.http, 04-polling.http, and 12-list-tasks.http.

Original SVG

Original source
id: fig-history-and-pages
kind: structure
title: history regimes and list pages
claim: `GetTask` trims history to the newest messages you ask for, and `ListTasks` returns newest-first pages that leave out artifacts until you ask for them.
caption: Top, the stored history of task e5c95b09 and what each `historyLength` returns. Bottom, the five tasks on test-runner in list order, grouped into the pages the kit requested. Ids are shortened to 8 characters. From capture/out/13-history.http, 04-polling.http, and 12-list-tasks.http.

The specification requires cursor-based pagination and gives the token no format {{spec §3.1.4}}, so each SDK makes its own:

SDK Token Page size Last page
a2a-python 1.2.2 URL-safe base64 of a JSON cursor with the status timestamp and the task id {{sdk src/a2a/utils/task.py}} 50 by default, 1 to 100 or error -32602 ""
a2a-go URL-safe base64 of the update time and the task id {{sdk-go a2asrv/taskstore/inmemory.go at v2.6.0}} 50 by default, 1 to 100 or error -32600 ""
a2a-java plain text, the status time in epoch milliseconds, a colon, and the task id {{sdk-java util/PageToken.java at v1.4.0.Final}} 50 by default, 1 to 100 or error -32602 ""
a2a-js base64 of the status timestamp, a bar, and the task id {{sdk-js src/server/utils.ts at v1.3.0}} 50 by default, 1 to 100 or error -32602 ""
a2a-dotnet a decimal offset such as 50 {{sdk-dotnet src/A2A/Server/InMemoryTaskStore.cs at v1.0.0-preview2}} 50 by default, and only JSON-RPC rejects a size outside 1 to 100 ""
a2a-rs a decimal offset over tasks sorted by id, against the order rule above {{sdk-rust a2a-server/src/task_store/inmemory.rs at a2a-server-lf-v0.5.1}} 50 by default, and a size above 100 is cut to 100 ""

Send every parameter

The third request sends no historyLength and gets the full history, as the reference SDK would {{sdk src/a2a/server/request_handlers/default_request_handler_v2.py}}. With 0, the SDK returns an empty history array where the specification asks for no field {{sdk src/a2a/server/routes/common.py}}. So treat a missing history and an empty one alike.

Pass back exactly the token you were given. The migration guide calls these fields cursor, limit, and nextCursor, names the proto does not define (conflict D12).

The bound of statusTimestampAfter is inclusive: the result holds only tasks whose status timestamp is at or after this value {{proto ListTasksRequest}}. To fetch only what changed, send the newest status timestamp you have seen, and skip the boundary task you already hold.

Original source
- Call `GetTask` after a stream ends, a webhook arrives, or your process restarts.
- Send `historyLength` on every read: 0 for a status check, a small number for the latest turn.
- Page with the exact token you were given until it comes back empty.
- Set `includeArtifacts` when a list must carry results, or fetch each task with `GetTask`.

Sources: spec §3.1.3, §3.1.4, §3.2.4, §3.7 (research/sources/specification.md); proto GetTaskRequest, ListTasksRequest (research/sources/a2a.proto); sdk src/a2a/server/tasks/task_manager.py, src/a2a/server/request_handlers/default_request_handler_v2.py, src/a2a/server/routes/common.py, src/a2a/utils/task.py (a2a-python 1.2.2); sdk-go a2asrv/handler.go, a2asrv/taskstore/inmemory.go at v2.6.0; sdk-java server-common/src/main/java/org/a2aproject/sdk/server/requesthandlers/DefaultRequestHandler.java, spec/src/main/java/org/a2aproject/sdk/spec/util/PageToken.java at v1.4.0.Final; sdk-js src/server/request_handler/default_request_handler.ts, src/server/utils.ts at v1.3.0; sdk-dotnet src/A2A/Server/A2AServer.cs, src/A2A/Server/InMemoryTaskStore.cs at v1.0.0-preview2; sdk-rust a2a-server/src/task_store/mod.rs, a2a-server/src/task_store/inmemory.rs at a2a-server-lf-v0.5.1; capture/out/04-polling.http, 12-list-tasks.http, 13-history.http

Code and artifacts 1
What you take away

Saved in your notebook on this device.

Sources and limits

Reading checkpoints are self-reported. Test results are recorded separately and apply to the executed cases.

AI Engineering from Scratch · 3f9715c · MIT

Download this file ↓