feat!: Proposal for bidirectional streaming over gRPC#1549
feat!: Proposal for bidirectional streaming over gRPC#1549HungYangChang wants to merge 9 commits intoa2aproject:mainfrom
Conversation
This adds a new `SendLiveMessage` RPC to the gRPC A2A service definition. The purpose of this RPC is to address cases where a client would like to maintain an active request stream with an agent, rather than requiring separate follow-up requests any time an agent needs further input. This bidirectional communication is ONLY supported via gRPC. I have tried to lay out the expected semantics as a comment in the gRPC specification. A2A is generally a turn-based protocol, where clients and servers take turns sending each other messages. This is not specifically encoded in our specification, however our SDK implementations do not directly support the idea of receiving a stream of messages from a client. Instead, each new message is treated as a new invocation of the agent implementation. The assumption here is that a client will only send messages to tasks that are in an interrupted state (i.e. input-required), and that agent executions will exit when they reach an interrupted state. The current behavior of SDKs for when a message is received for a task that is actively being processed could be categorized as "undefined". A bidirectional endpoint opens the possibility of full-duplex communication between client and server, however it doesn't require it: bidirectionality is still useful even in a turn-based protocol. The benefit is that an ongoing connection can be maintained for sending responses to agents that enter interrupted states, which enables several convenient properties: - Agent implementations can more easily "await" responses inline, rather than needing to save all necessary state, exit, then reconstitute state when a response is received. - Clients can perform less state tracking, particularly around specifying task and context IDs. These can be implicit to the connection, as a connection is only valid for a single task. - Distributed agent implementations don't need to implement clever load-balancing/routing to achieve task processing locality. Since an active connection is maintained, all responses are received by the same server. This is another view of the first point in this list, but from the networking layer.
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Move bidi_streaming (field 4) after extensions (field 3) to follow the convention of ordering fields by their field number. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Add SendLiveMessage RPC and bidi_streaming capability to the current specification/a2a.proto instead of the old deleted file locations. Remove re-created legacy files that no longer exist in upstream. Co-Authored-By: Claude Opus 4.6 <[email protected]>
- specification.md: Add Section 3.1.3 (Send Live Message operation), capability validation bullet, method mapping row, gRPC method subsection (10.4.3), and update streaming section (10.7) - streaming-and-async.md: Add bidirectional streaming section with comparison table and usage guidance - key-concepts.md: Add bidirectional streaming to interaction mechanisms - llms.txt: Add SendLiveMessage to RPC table and bidi_streaming to capabilities list - whats-new-v1.md: Document new bidiStreaming capability and SendLiveMessage RPC - extensions.md: Fix broken snippet include (types/src/types.ts was deleted, replaced with proto reference) Co-Authored-By: Claude Opus 4.6 <[email protected]>
Summary of ChangesHello @HungYangChang, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the A2A protocol by introducing bidirectional streaming capabilities through a new Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This is an excellent and well-documented proposal for adding bidirectional streaming to the A2A protocol. The new SendLiveMessage RPC is clearly defined, and the extensive updates to the specification and topic guides provide great clarity on its use cases, particularly for human-in-the-loop scenarios. The changes are consistent and follow the protocol's design principles. I have only a couple of minor suggestions to enhance the documentation around reconnection scenarios to make it even more comprehensive for implementers.
Add NOT_FOUND and FAILED_PRECONDITION error cases to reconnection docs in specification.md and streaming-and-async.md per gemini-code-assist suggestions. Co-Authored-By: Claude Opus 4.6 <[email protected]>
499209e to
ecdea3f
Compare
Inserting section 3.1.3 (Send Live Message) shifted all subsequent sections. This fixes the heading numbers (3.1.4 List Tasks through 3.1.12 Get Extended Agent Card) and updates all internal link fragments to match, resolving markdownlint MD051 errors. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Reframe bidirectional streaming documentation to emphasize persistent connections and simplified state management rather than human-in-the-loop interactions, aligning with the original PR a2aproject#1120 framing. Co-Authored-By: Claude Opus 4.6 <[email protected]>
…_table macro Co-Authored-By: Claude Opus 4.6 <[email protected]>
|
Hi @Tehsmash Will you be willing /the right person to review this PR? |
| bidirectional stream with the agent, enabling inline responses during | ||
| interrupted states (e.g., `input-required`) without reconnecting. | ||
| This simplifies state management for both agents and clients in | ||
| interactive task flows. gRPC-only; requires `capabilities.bidiStreaming`. |
There was a problem hiding this comment.
I wouldn't claim "gRPC-only" here, A2A supports pluggable protocol bindings which may or may not support bidirectional streaming.
We have also previously claimed that if an Agent lists a capability that all protocol bindings supported by that Agent MUST support all the capabilities so setting bidiStreaming=True means that all transports support by the agent MUST support it.
|
|
||
| - **Initiating a Stream:** The client calls the `SendLiveMessage` RPC, which accepts a stream of `SendMessageRequest` objects and returns a stream of `StreamResponse` objects. | ||
|
|
||
| - **Interrupted States:** When the agent transitions a task to `INPUT_REQUIRED` or `AUTH_REQUIRED`, the stream remains open. The client sends a follow-up `SendMessageRequest` on the same stream with the additional input. The agent then transitions back to `WORKING` and continues processing. |
There was a problem hiding this comment.
The AUTH_REQUIRED state does not necessarily require a follow up message to be sent as it can be resolved out of band. So it would be better like:
| - **Interrupted States:** When the agent transitions a task to `INPUT_REQUIRED` or `AUTH_REQUIRED`, the stream remains open. The client sends a follow-up `SendMessageRequest` on the same stream with the additional input. The agent then transitions back to `WORKING` and continues processing. | |
| - **Interrupted States:** When the agent transitions a task to `INPUT_REQUIRED` or `AUTH_REQUIRED`, the stream remains open. The client can then send any required follow-up `SendMessageRequest` on the same stream. The agent may then transition back to `WORKING` and continues processing and return events on the stream. |
|
|
||
| - **Stream Termination:** The agent MUST half-close the connection when the task reaches a terminal state (`COMPLETED`, `FAILED`, `CANCELED`, `REJECTED`). | ||
|
|
||
| - **Reconnection:** A client can reconnect to an ongoing task by calling `SendLiveMessage` with a `taskId` and empty message parts. If the task is active, the agent sends the current state and resumes streaming updates. If the task is already in a terminal state, the agent returns the final task details and half-closes the stream. If the task ID is not found, or if another stream is already active and the agent doesn't support multiplexing, an error (`NOT_FOUND` or `FAILED_PRECONDITION`) is returned. |
There was a problem hiding this comment.
I'm not sure this is consistent with the current A2A protocol, I think it would be better to have a "live" equivalent of the SubscribeToTask method something like "ReconnectToTask"
| | :--- | :--- | :--- | :--- | | ||
| | `SendMessage` | `SendMessageRequest` | `SendMessageResponse` | Initiates or continues a task. | | ||
| | `SendStreamingMessage` | `SendMessageRequest` | `stream StreamResponse` | Sends message and receives real-time SSE updates. | | ||
| | `SendLiveMessage` | `stream SendMessageRequest` | `stream StreamResponse` | Bidirectional streaming for live agent interaction (gRPC only). | |
There was a problem hiding this comment.
| | `SendLiveMessage` | `stream SendMessageRequest` | `stream StreamResponse` | Bidirectional streaming for live agent interaction (gRPC only). | | |
| | `SendLiveMessage` | `stream SendMessageRequest` | `stream StreamResponse` | Bidirectional streaming for live agent interaction.| |
There was a problem hiding this comment.
This will not be included in v1.0, no need for this doc update.
| **Reconnection:** | ||
|
|
||
| A client may reconnect to an ongoing task by specifying a `taskId` with empty message parts. If the task is in a terminal state, the agent returns the task and half-closes. If the task is active, the agent sends the current state and begins sending updates. If no task exists with the specified ID, the agent SHOULD return a `NOT_FOUND` error. If another client already has an active stream for the task and the agent does not support multiple connections, the agent MAY return a `FAILED_PRECONDITION` error. |
There was a problem hiding this comment.
As described in my comment on "streaming-and-async.md" I don't think this aligns well with the existing A2A protocol design and the behaviour of the other methods. I think we should introduced a "ReconnectToTask" method as the bidi alternative to SubscribeToTask.
|
|
||
| - **Push Notifications**: If `AgentCard.capabilities.pushNotifications` is `false` or not present, operations related to push notification configuration (Create, Get, List, Delete) **MUST** return [`PushNotificationNotSupportedError`](#332-error-handling). | ||
| - **Streaming**: If `AgentCard.capabilities.streaming` is `false` or not present, attempts to use `SendStreamingMessage` or `SubscribeToTask` operations **MUST** return [`UnsupportedOperationError`](#332-error-handling). | ||
| - **Bidirectional Streaming**: If `AgentCard.capabilities.bidiStreaming` is `false` or not present, attempts to use `SendLiveMessage` **MUST** return `UNIMPLEMENTED` (gRPC). This capability is only applicable to the gRPC protocol binding. |
There was a problem hiding this comment.
This should align with other capabilities and return the UnsupportedOperationError.
| - **Bidirectional Streaming**: If `AgentCard.capabilities.bidiStreaming` is `false` or not present, attempts to use `SendLiveMessage` **MUST** return `UNIMPLEMENTED` (gRPC). This capability is only applicable to the gRPC protocol binding. | |
| - **Bidirectional Streaming**: If `AgentCard.capabilities.bidiStreaming` is `false` or not present, attempts to use `SendLiveMessage` **MUST** return [`UnsupportedOperationError`](#332-error-handling). |
|
|
||
| - Real-time delivery of events as they occur | ||
| - Operations: Stream Message ([Section 3.1.2](#312-send-streaming-message)) and Subscribe to Task ([Section 3.1.6](#316-subscribe-to-task)) | ||
| - Operations: Stream Message ([Section 3.1.2](#312-send-streaming-message)), Subscribe to Task ([Section 3.1.7](#317-subscribe-to-task)), and Live Message ([Section 3.1.3](#313-send-live-message-bidirectional-streaming), gRPC only) |
There was a problem hiding this comment.
| - Operations: Stream Message ([Section 3.1.2](#312-send-streaming-message)), Subscribe to Task ([Section 3.1.7](#317-subscribe-to-task)), and Live Message ([Section 3.1.3](#313-send-live-message-bidirectional-streaming), gRPC only) | |
| - Operations: Stream Message ([Section 3.1.2](#312-send-streaming-message)), Subscribe to Task ([Section 3.1.7](#317-subscribe-to-task)), and Live Message ([Section 3.1.3](#313-send-live-message-bidirectional-streaming)) |
| - Best for: Interactive applications, real-time dashboards, live progress monitoring | ||
| - Requires `AgentCard.capabilities.streaming` to be `true` | ||
| - `SendStreamingMessage`/`SubscribeToTask` require `AgentCard.capabilities.streaming` to be `true` | ||
| - `SendLiveMessage` (bidirectional) requires `AgentCard.capabilities.bidiStreaming` to be `true` and is gRPC-only. Unlike server-streaming, the stream stays open during interrupted states for inline client responses |
There was a problem hiding this comment.
| - `SendLiveMessage` (bidirectional) requires `AgentCard.capabilities.bidiStreaming` to be `true` and is gRPC-only. Unlike server-streaming, the stream stays open during interrupted states for inline client responses | |
| - `SendLiveMessage` (bidirectional) requires `AgentCard.capabilities.bidiStreaming` to be `true`. Unlike server-streaming, the stream stays open during interrupted states for inline client responses |
| | :------------------------------ | :--------------------------------- | :--------------------------------- | :------------------------------------------------------ | | ||
| | Send message | `SendMessage` | `SendMessage` | `POST /message:send` | | ||
| | Stream message | `SendStreamingMessage` | `SendStreamingMessage` | `POST /message:stream` | | ||
| | Live message (bidi streaming) | N/A | `SendLiveMessage` | N/A | |
There was a problem hiding this comment.
I think we might need to add POST /message:live for the HTTP protocol binding and "SendLiveMessage" for JSONRPC so they can return the UnsupportedError.
Follow on PR #1120 - BiDi Streaming Discussion Responses
PR: feat!: Proposal for bidirectional streaming over gRPC
Original Author: mikeas1| Author: hungyangchang| Status: Open,
TSC ReviewSummary
Adds
SendLiveMessagebidirectional streaming RPC to A2A gRPC. Clients maintain an active stream with agents instead of separate follow-up requests. Benefits: inline await, less client state tracking, simpler distributed routing.Upstream File Structure Changes
The original PR modified 3 files that no longer exist in upstream
main:specification/grpc/a2a.protospecification/a2a.protospecification/json/a2a.jsontypes/src/types.tsa2a.protois now the single source of truth. The JSON schema is generated at build time viascripts/proto_to_json_schema.sh, anddocs/specification.mdusesproto_to_table()macros that auto-render from proto definitions.Responses
1. gemini-code-assist (bot) - Oct 1, 2025
Field ordering: Fixed.
bidi_streamingnow followsextensionsby field number. In new schema it's field 5 (field 4 =extended_agent_card).FAILED_PRECONDITIONvsUNIMPLEMENTED: Both are used correctly for distinct cases.FAILED_PRECONDITION= duplicate active stream;UNIMPLEMENTED= method not supported.Omitted
SendMessageConfiguration: Already specified: "If omitted, the agent MUST continue to use the configuration from the initial request."2. ToddSegal - Oct 7, 2025
Resubscribe interaction: Continues as-is.
SendLiveMessageis a separate RPC.Multiple client calls: Three rules (per mikeas1):
FAILED_PRECONDITIONMissing task_id: Empty values are interpreted as the established stream's task_id/context_id.
3. HungYangChang - Dec 11, 2025
Interrupted vs Failed: These are distinct states with different BiDi behavior:
INPUT_REQUIRED,AUTH_REQUIREDCOMPLETED,FAILED,CANCELED,REJECTEDThis is the key difference from
SendStreamingMessage, which closes on both interrupted and terminal states (per spec 11.1: "until the task reaches a terminal or interrupted state, at which point the stream closes").State transitions on BiDi stream: When a Task reaches
INPUT_REQUIRED:The agent can "await" the response inline without saving/reconstituting state. This is the primary advantage over
SendStreamingMessage+ separateSendMessagefollow-up.Timeout: Not specified in the protocol. Left to agent implementation, consistent with A2A's approach of leaving operational policies to implementations. Options:
FAILEDorCANCELEDtimeoutfield inSendMessageConfigurationMultiple interruptions: Fully supported. Each interruption keeps the stream open:
Error codes:
UNIMPLEMENTEDNOT_FOUNDFAILED_PRECONDITIONINVALID_ARGUMENT4. Tehsmash - Feb 25, 2026
Concern: BiDi might encourage multi-turn tied to a single Task instead of context-based reuse.
Response: This concern is partially valid, and we should be clear about scope:
The BiDi stream IS scoped to a single Task. The spec states: "the established stream Task" (singular), and "The agent MUST half-close the connection when the Task reaches a terminal state." Once a task completes, the stream closes. You cannot create Task B on the same stream after Task A completes.
BiDi is NOT for multi-turn conversation. The recommended pattern for multi-turn remains:
This is unchanged. BiDi doesn't replace or conflict with this pattern.
BiDi IS for single-task human-in-the-loop. The specific use case is tasks that need clarification or approval mid-execution:
Without BiDi, this requires:
SendStreamingMessage-> stream closes onINPUT_REQUIRED-> newSendMessage-> newSendStreamingMessageto get updates. BiDi simplifies this to a single persistent connection.The "bad" pattern Tehsmash described isn't really bad for BiDi. The pattern
Task -> input-required -> respond -> input-required -> respond -> completeis exactly the intended use case. It represents a single unit of work (e.g., booking a flight) that requires multiple human confirmations. This is different from multi-turn conversation where each turn is a separate task.Key distinction to document: BiDi streaming is for single-task execution with inline interruptions, not for multi-task conversational flows.
Recommended BiDi Streaming Patterns
When to Use
SendLiveMessage(BiDi)Use BiDi streaming when all of these apply:
capabilities.bidiStreaming: trueINPUT_REQUIREDorAUTH_REQUIREDstatesWhen NOT to Use
SendLiveMessageSendMessageinsteadSendStreamingMessageSendMessagecalls with sharedcontextIdSendMessage+ push notificationsPattern 1: Single-Task with Human Approval (Recommended BiDi Use Case)
A task that requires one or more human confirmations before completing.
Pattern 2: Multi-Turn Conversation (Do NOT Use BiDi)
For multi-turn conversations, use separate RPC calls with shared
contextId:Pattern 3: Reconnecting to an Active Task
A client can reconnect to an ongoing task's BiDi stream (e.g., after network disruption):
Comparison Table
SendMessageSendStreamingMessageSendLiveMessageSendMessage+ push notificationsSendMessagecallsChanges Made
Proto (
specification/a2a.proto)SendLiveMessagebidirectional streaming RPC afterSendStreamingMessageoptional bool bidi_streaming = 5toAgentCapabilitiesspecification/grpc/a2a.proto,specification/json/a2a.json,types/src/types.ts)Specification (
docs/specification.md)bidiStreamingcapability validation bullet in Section 3.3.4SendLiveMessagerow to method mapping table in Section 5.3 (gRPC only, N/A for JSON-RPC and REST)SendLiveMessageSendLiveMessageto operations layer Mermaid diagramTopic Docs
docs/topics/streaming-and-async.md: Added "Bidirectional Streaming with gRPC" section with SSE vs BiDi comparison table, usage guidance, and when to/not to usedocs/topics/key-concepts.md: Added bidirectional streaming as 4th interaction mechanismReference Docs
docs/llms.txt: AddedSendLiveMessageto RPC methods table,bidi_streamingto capabilities listdocs/whats-new-v1.md: AddedbidiStreamingto AgentCapabilities changes, added "NEW: SendLiveMessage" operation sectionBug Fix
docs/topics/extensions.md: Fixed broken snippet include —types/src/types.ts:AgentExtension(deleted file) replaced withspecification/a2a.proto:AgentExtensionAction Items
AgentCapabilitiesspecification/a2a.proto(new file location)