Skip to content

feat: Enforce Task or Message as an initial response for Send Streaming Message#964

Closed
sokoliva wants to merge 7 commits intoa2aproject:1.0-devfrom
sokoliva:update-cli
Closed

feat: Enforce Task or Message as an initial response for Send Streaming Message#964
sokoliva wants to merge 7 commits intoa2aproject:1.0-devfrom
sokoliva:update-cli

Conversation

@sokoliva
Copy link
Copy Markdown
Member

@sokoliva sokoliva commented Apr 13, 2026

Description

The A2A spec (§3.1.2) requires that the first event in a streaming response is always a Task or Message object. However, agents commonly emit TaskStatusUpdateEvent(WORKING) as their first event without explicitly emitting a Task first. Previously the v2 handler forwarded these events as-is, violating the spec.

Changes

  • default_request_handler_v2.py: when the v2 handler (DefaultRequestHandlerV2) receives a new streaming request and the agent's first event is not a Task or Message, it now injects a Task(SUBMITTED) before forwarding the agent's event.
  • update samples/cli.py to reflect changes in send_message return type
  • update samples/cli.py to expect Task or Message as first streaming event
  • add a README.md for samples
  • utilize get_artifact_text, get_message_text helpers in samples/cli.py

Tested

Changes seen in tests/integration/test_scenarios.py

uv run samples/cli.py
Connecting to http://127.0.0.1:41241 (preferred transport: Any)

✓ Agent Card Found:
  Name: Sample Agent
  Picked Transport: JsonRpcTransport

Connected! Send a message or type /quit to exit.
You: hi
Task [state=TASK_STATE_SUBMITTED]
TaskStatusUpdate [state=TASK_STATE_WORKING]: Processing your question...
TaskArtifactUpdate [name=response]: Hello World! Nice to meet you!
TaskStatusUpdate [state=TASK_STATE_COMPLETED]: 
--- Task Completed ---
You: bye
Task [state=TASK_STATE_SUBMITTED]
TaskStatusUpdate [state=TASK_STATE_WORKING]: Processing your question...
TaskArtifactUpdate [name=response]: Goodbye! Have a wonderful day!
TaskStatusUpdate [state=TASK_STATE_COMPLETED]: 
--- Task Completed ---
You: /quit

Note

The legacy handler (DefaultRequestHandler) is intentionally left unchanged to avoid breaking existing users who consume its streaming output directly. Also, spec v0.3 does not define the initial event in send stream message.

Fixes: #965

@sokoliva sokoliva requested a review from a team as a code owner April 13, 2026 10:23
@sokoliva sokoliva requested a review from guglielmo-san April 13, 2026 10:26
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 13, 2026

🧪 Code Coverage (vs 1.0-dev)

⬇️ Download Full Report

Base PR Delta
src/a2a/server/request_handlers/default_request_handler_v2.py 92.67% 92.95% 🟢 +0.27%
Total 92.49% 92.50% 🟢 +0.01%

Generated by coverage-comment.yml

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a README for the samples directory and refactors the CLI sample to use utility functions for message and artifact text extraction. It also adds gRPC channel factory configuration. Feedback includes correcting the sample count in the documentation and improving the robustness of task ID assignment in the CLI while removing redundant checks.

Comment thread samples/README.md
Comment thread samples/cli.py Outdated
Comment thread samples/cli.py Outdated
@sokoliva sokoliva changed the title fix: update samples/cli.py feat: Enforce Task or Message as an initial response for Send Streaming Message Apr 13, 2026
@sokoliva sokoliva requested a review from guglielmo-san April 13, 2026 14:58
Comment on lines +327 to +332
submitted_task = Task(
id=task_id,
context_id=context_id,
status=TaskStatus(state=TaskState.TASK_STATE_SUBMITTED),
history=[params.message],
)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The task_manager is already covering this use case, we can use the task created and saved on the task store

async def ensure_task_id(self, task_id: str, context_id: str) -> Task:

@sokoliva
Copy link
Copy Markdown
Member Author

Proceeding with a different solution - should emit error.

@sokoliva sokoliva closed this Apr 16, 2026
@sokoliva sokoliva deleted the update-cli branch April 16, 2026 15:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants