Repository navigation
fix(a2a): return GetTask and CancelTask tasks in result - #595
Merged
Merged
Conversation
The A2A spec defines GetTask and CancelTask as returning a Task, so the
JSON-RPC result is the Task itself. aimock wrapped it as { task }, and
the @a2a-js/sdk client decoded an empty Task (no id, status or history).
Move GetTask/CancelTask assertions off the result.task wrapper and add a regression test that pins the spec shape for both methods.
commit: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bug
The A2A spec defines
GetTaskandCancelTaskas returning aTask, so the JSON-RPCresultis the Task itself. aimock returnedresult: { task: {...} }. The official@a2a-js/sdkclient (1.3.0) callsTask.fromJSON(rpcResponse.result), which reads top-levelid,contextIdandstatus. With the wrapper, everyclient.getTask()andclient.cancelTask()decoded an empty Task: no id, no status, no history.Fix
src/a2a-handler.ts:GetTaskandCancelTaskreturn the stored task directly inresult.This changes the wire shape (bug fix). Anyone reading
result.taskfrom a GetTask or CancelTask response must readresultinstead. SendMessage (result.task/result.message) and ListTasks (result.tasks) are unchanged. Those shapes already match the spec.Local red/green proof
The probe starts a real
A2AMockfrom the builtdist, creates a task fixture withSendMessage, and callsgetTask. It also opens a delayed streaming task and callscancelTaskwhile that task is WORKING. Both calls go through the@a2a-js/sdk1.3.0ClientFactoryJSON-RPC client. Raw bodies are shown too. Timestamps are redacted.RED (origin/main):
GREEN (this branch):
Updated assertions
These existing tests in
src/__tests__/a2a-mock.test.tsread GetTask/CancelTask responses. They move frombody.result.task.*tobody.result.*:result.tasktoBeDefined check is nowresult.id === taskId, and the follow-up GetTask check is updated the same way.New regression test: "returns the Task itself as result for GetTask and CancelTask (A2A spec shape)". It asserts that there is no
taskkey and that id, contextId, status, artifacts and history are at the top level ofresult. With the new test file against the old handler, 6 tests fail: the new test plus the 5 updated ones. All pass with the fix.Other-method audit (reported, not fixed here)
These were checked against the
@a2a-js/sdk1.3.0 decoders (SendMessageResponse,StreamResponse,ListTasksResponse,Task,Artifact):{task}and{message}matchSendMessageResponse. The task result leaves outhistory, which is optional.{tasks:[...]}matchesListTasksResponse.nextPageToken,pageSizeandtotalSizeare absent, and the SDK defaults them.{task:{...}, artifact:{...}}.StreamResponse.fromJSONpicks thetaskcase first, so the SDK drops the artifact on every artifact event. The spec shape is{artifactUpdate:{taskId, contextId, artifact, append, lastChunk}}.{task:{id,contextId,status}}snapshots instead of{statusUpdate:{taskId,contextId,status}}. The SDK decodes them as Task events, so this works, but it does not follow the spec.artifactId, which A2A requires. The SDK decodes it as"".statusevent overwritesTASK_STATE_CANCELED, and the task endsTASK_STATE_COMPLETED(seen in the probe).Checks
Full suite: 484 files, 19704 passed and 0 failed. Typecheck, lint, prettier and build are clean. The api report has no change, because no public types changed. Docs: no page documents the GetTask/CancelTask result shape, so none needed an update.