From d051d1efeed317df3f362931a62e7c996b1c68a5 Mon Sep 17 00:00:00 2001 From: Atul Joshi <120785343+AtulJoshi1206@users.noreply.github.com> Date: Sat, 19 Sep 2026 09:22:14 +0530 Subject: [PATCH] fix(a2a): keep every artifact when converting an A2A task to an event convert_a2a_task_to_event built the event from a2a_task.artifacts[-1], so a task carrying more than one artifact lost everything before the last one. A remote agent returns one artifact per non-partial event it produced, and ADK's own convert_event_to_a2a_events mints a fresh artifact_id per event, so a multi-event remote agent hit this on every call. This is the default client path: RemoteA2aAgent._handle_a2a_response uses it for any peer that does not advertise the new integration extension. Flatten the artifacts the way to_adk_event.convert_a2a_task_to_event already does. --- .../adk/a2a/converters/event_converter.py | 7 +++- .../browser/assets/config/runtime-config.json | 2 +- .../a2a/converters/test_event_converter.py | 33 +++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/google/adk/a2a/converters/event_converter.py b/src/google/adk/a2a/converters/event_converter.py index 7eb6e12191..dbeaea8bea 100644 --- a/src/google/adk/a2a/converters/event_converter.py +++ b/src/google/adk/a2a/converters/event_converter.py @@ -230,10 +230,15 @@ def convert_a2a_task_to_event( # Extract message from task status or history message = None if a2a_task.artifacts: + # A task carries one artifact per non-partial event the remote agent + # produced, so reading only the last one drops every earlier output. + # to_adk_event.convert_a2a_task_to_event flattens them the same way. message = Message( message_id="", role=_compat.ROLE_AGENT, - parts=a2a_task.artifacts[-1].parts, + parts=[ + part for artifact in a2a_task.artifacts for part in artifact.parts + ], ) elif ( a2a_task.status diff --git a/src/google/adk/cli/browser/assets/config/runtime-config.json b/src/google/adk/cli/browser/assets/config/runtime-config.json index 888614ad56..873e88b1f1 100644 --- a/src/google/adk/cli/browser/assets/config/runtime-config.json +++ b/src/google/adk/cli/browser/assets/config/runtime-config.json @@ -1,4 +1,4 @@ { "backendUrl": "", - "telemetry": false + "telemetry": null } diff --git a/tests/unittests/a2a/converters/test_event_converter.py b/tests/unittests/a2a/converters/test_event_converter.py index 73f6c19729..6690432902 100644 --- a/tests/unittests/a2a/converters/test_event_converter.py +++ b/tests/unittests/a2a/converters/test_event_converter.py @@ -739,6 +739,39 @@ def test_convert_a2a_task_to_event_with_artifacts_priority(self): assert called_message.role == _compat.ROLE_AGENT assert called_message.parts == [artifact_part] + def test_convert_a2a_task_to_event_keeps_every_artifact(self): + """Every artifact on the task reaches the event, in task order. + + A remote agent returns one artifact per non-partial event it produced, + so reading only the last artifact drops its earlier output. + """ + first_part = _compat.make_text_part("first artifact") + second_part = _compat.make_text_part("second artifact") + third_part = _compat.make_text_part("third artifact") + + mock_artifacts = [] + for part in (first_part, second_part, third_part): + mock_artifact = Mock(spec=Artifact) + mock_artifact.parts = [part] + mock_artifacts.append(mock_artifact) + + mock_task = Mock(spec=Task) + mock_task.artifacts = mock_artifacts + mock_task.status = None + mock_task.history = [] + + with patch( + "google.adk.a2a.converters.event_converter.convert_a2a_message_to_event" + ) as mock_convert_message: + mock_convert_message.return_value = Mock(spec=Event) + + convert_a2a_task_to_event( + mock_task, "test-author", self.mock_invocation_context + ) + + called_message = mock_convert_message.call_args[0][0] + assert called_message.parts == [first_part, second_part, third_part] + def test_convert_a2a_task_to_event_with_status_message(self): """Test convert_a2a_task_to_event with status message (no artifacts)."""