@@ -261,10 +261,21 @@ async def cancel(
261261 event
262262 async for event in client .send_message (SendMessageRequest (message = msg ))
263263 ]
264- assert [event .status_update .status .state for event in events ] == [
265- TaskState .TASK_STATE_WORKING ,
266- TaskState .TASK_STATE_COMPLETED ,
267- ]
264+ if use_legacy :
265+ # Legacy handler streams events as-is (no Task(SUBMITTED) injection).
266+ assert [event .status_update .status .state for event in events ] == [
267+ TaskState .TASK_STATE_WORKING ,
268+ TaskState .TASK_STATE_COMPLETED ,
269+ ]
270+ else :
271+ # V2 handler injects Task(SUBMITTED) first per A2A spec §3.1.2.
272+ assert events [0 ].HasField ('task' ), (
273+ 'First streaming event must be a Task or Message'
274+ )
275+ assert [event .status_update .status .state for event in events [1 :]] == [
276+ TaskState .TASK_STATE_WORKING ,
277+ TaskState .TASK_STATE_COMPLETED ,
278+ ]
268279
269280
270281# Scenario 5: Re-subscribing to a finished task
@@ -374,15 +385,32 @@ async def cancel(
374385 configuration = SendMessageConfiguration (return_immediately = False ),
375386 )
376387 )
377- ( event ,) = [event async for event in it ]
388+ events = [event async for event in it ]
378389
379390 if streaming :
380- assert event .HasField ('status_update' )
381- task_id = event .status_update .task_id
382- assert (
383- event .status_update .status .state == TaskState .TASK_STATE_COMPLETED
384- )
391+ if use_legacy :
392+ # Legacy streams events as-is: just the status_update(COMPLETED).
393+ (event ,) = events
394+ assert event .HasField ('status_update' )
395+ task_id = event .status_update .task_id
396+ assert (
397+ event .status_update .status .state
398+ == TaskState .TASK_STATE_COMPLETED
399+ )
400+ else :
401+ # V2 injects Task(SUBMITTED) first per A2A spec §3.1.2.
402+ assert len (events ) == 2
403+ assert events [0 ].HasField ('task' ), (
404+ 'First streaming event must be a Task or Message'
405+ )
406+ task_id = events [0 ].task .id
407+ assert events [1 ].HasField ('status_update' )
408+ assert (
409+ events [1 ].status_update .status .state
410+ == TaskState .TASK_STATE_COMPLETED
411+ )
385412 else :
413+ (event ,) = events
386414 assert event .HasField ('task' )
387415 task_id = event .task .id
388416 assert event .task .status .state == TaskState .TASK_STATE_COMPLETED
@@ -498,8 +526,23 @@ async def cancel(
498526 tasks = []
499527
500528 if streaming :
501- res = await it .__anext__ ()
502- assert res .status_update .status .state == TaskState .TASK_STATE_WORKING
529+ if use_legacy :
530+ # Legacy streams events as-is; first event is the WORKING status update.
531+ first = await it .__anext__ ()
532+ assert (
533+ first .status_update .status .state == TaskState .TASK_STATE_WORKING
534+ )
535+ else :
536+ # V2 injects Task(SUBMITTED) first per A2A spec §3.1.2.
537+ first = await it .__anext__ ()
538+ assert first .HasField ('task' ), (
539+ 'First streaming event must be a Task or Message'
540+ )
541+ second = await it .__anext__ ()
542+ assert (
543+ second .status_update .status .state
544+ == TaskState .TASK_STATE_WORKING
545+ )
503546 continue_event .set ()
504547 else :
505548
@@ -1082,10 +1125,19 @@ async def cancel(
10821125 states = [get_state (event ) async for event in it ]
10831126
10841127 if streaming :
1085- assert states == [
1086- TaskState .TASK_STATE_WORKING ,
1087- TaskState .TASK_STATE_COMPLETED ,
1088- ]
1128+ if use_legacy :
1129+ # Legacy streams events as-is (no Task(SUBMITTED) injection).
1130+ assert states == [
1131+ TaskState .TASK_STATE_WORKING ,
1132+ TaskState .TASK_STATE_COMPLETED ,
1133+ ]
1134+ else :
1135+ # V2 injects Task(SUBMITTED) first per A2A spec §3.1.2.
1136+ assert states == [
1137+ TaskState .TASK_STATE_SUBMITTED ,
1138+ TaskState .TASK_STATE_WORKING ,
1139+ TaskState .TASK_STATE_COMPLETED ,
1140+ ]
10891141 else :
10901142 assert states == [TaskState .TASK_STATE_WORKING ]
10911143
@@ -1151,11 +1203,27 @@ async def cancel(
11511203 )
11521204
11531205 events1 = [event async for event in it ]
1154- assert [get_state (event ) for event in events1 ] == [
1155- TaskState .TASK_STATE_INPUT_REQUIRED ,
1156- ]
1157- task_id = events1 [0 ].status_update .task_id
1158- context_id = events1 [0 ].status_update .context_id
1206+ if streaming and not use_legacy :
1207+ # V2 injects Task(SUBMITTED) first per A2A spec §3.1.2.
1208+ assert [get_state (event ) for event in events1 ] == [
1209+ TaskState .TASK_STATE_SUBMITTED ,
1210+ TaskState .TASK_STATE_INPUT_REQUIRED ,
1211+ ]
1212+ task_id = events1 [0 ].task .id
1213+ context_id = events1 [0 ].task .context_id
1214+ elif streaming and use_legacy :
1215+ # Legacy streams events as-is; first event is the INPUT_REQUIRED status update.
1216+ assert [get_state (event ) for event in events1 ] == [
1217+ TaskState .TASK_STATE_INPUT_REQUIRED ,
1218+ ]
1219+ task_id = events1 [0 ].status_update .task_id
1220+ context_id = events1 [0 ].status_update .context_id
1221+ else :
1222+ assert [get_state (event ) for event in events1 ] == [
1223+ TaskState .TASK_STATE_INPUT_REQUIRED ,
1224+ ]
1225+ task_id = events1 [0 ].task .id
1226+ context_id = events1 [0 ].task .context_id
11591227
11601228 # Now send another message to resume
11611229 msg2 = Message (
@@ -1240,19 +1308,38 @@ async def cancel(
12401308 )
12411309
12421310 if streaming :
1243- event1 = await asyncio .wait_for (it .__anext__ (), timeout = 1.0 )
1244- assert get_state (event1 ) == TaskState .TASK_STATE_WORKING
1311+ if use_legacy :
1312+ # Legacy streams events as-is: WORKING → AUTH_REQUIRED → COMPLETED.
1313+ event1 = await asyncio .wait_for (it .__anext__ (), timeout = 1.0 )
1314+ assert get_state (event1 ) == TaskState .TASK_STATE_WORKING
12451315
1246- event2 = await asyncio .wait_for (it .__anext__ (), timeout = 1.0 )
1247- assert get_state (event2 ) == TaskState .TASK_STATE_AUTH_REQUIRED
1316+ event2 = await asyncio .wait_for (it .__anext__ (), timeout = 1.0 )
1317+ assert get_state (event2 ) == TaskState .TASK_STATE_AUTH_REQUIRED
12481318
1249- task_id = event2 .status_update .task_id
1319+ task_id = event2 .status_update .task_id
12501320
1251- side_channel_event .set ()
1321+ side_channel_event .set ()
1322+
1323+ (event3 ,) = [event async for event in it ]
1324+ assert get_state (event3 ) == TaskState .TASK_STATE_COMPLETED
1325+ else :
1326+ # V2 injects Task(SUBMITTED) first per A2A spec §3.1.2.
1327+ # Full sequence: Task(SUBMITTED) → WORKING → AUTH_REQUIRED → COMPLETED.
1328+ event1 = await asyncio .wait_for (it .__anext__ (), timeout = 1.0 )
1329+ assert get_state (event1 ) == TaskState .TASK_STATE_SUBMITTED
1330+
1331+ event2 = await asyncio .wait_for (it .__anext__ (), timeout = 1.0 )
1332+ assert get_state (event2 ) == TaskState .TASK_STATE_WORKING
1333+
1334+ event3 = await asyncio .wait_for (it .__anext__ (), timeout = 1.0 )
1335+ assert get_state (event3 ) == TaskState .TASK_STATE_AUTH_REQUIRED
1336+
1337+ task_id = event3 .status_update .task_id
1338+
1339+ side_channel_event .set ()
12521340
1253- # Remaining event.
1254- (event3 ,) = [event async for event in it ]
1255- assert get_state (event3 ) == TaskState .TASK_STATE_COMPLETED
1341+ (event4 ,) = [event async for event in it ]
1342+ assert get_state (event4 ) == TaskState .TASK_STATE_COMPLETED
12561343 else :
12571344 (event ,) = [event async for event in it ]
12581345 assert get_state (event ) == TaskState .TASK_STATE_AUTH_REQUIRED
0 commit comments