Skip to content

feat!: Proposal for bidirectional streaming over gRPC#1549

Open
HungYangChang wants to merge 9 commits intoa2aproject:mainfrom
HungYangChang:live-rpc-rebased
Open

feat!: Proposal for bidirectional streaming over gRPC#1549
HungYangChang wants to merge 9 commits intoa2aproject:mainfrom
HungYangChang:live-rpc-rebased

Conversation

@HungYangChang
Copy link

Follow on PR #1120 - BiDi Streaming Discussion Responses

PR: feat!: Proposal for bidirectional streaming over gRPC
Original Author: mikeas1| Author: hungyangchang| Status: Open, TSC Review

Summary

Adds SendLiveMessage bidirectional 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:

File Old (PR #1120) Current upstream Action
specification/grpc/a2a.proto Modified Moved to specification/a2a.proto Changes applied to new location
specification/json/a2a.json Modified Auto-generated from proto, not in git None needed
types/src/types.ts Modified Removed entirely None needed

a2a.proto is now the single source of truth. The JSON schema is generated at build time via scripts/proto_to_json_schema.sh, and docs/specification.md uses proto_to_table() macros that auto-render from proto definitions.


Responses

1. gemini-code-assist (bot) - Oct 1, 2025

Field ordering: Fixed. bidi_streaming now follows extensions by field number. In new schema it's field 5 (field 4 = extended_agent_card).

FAILED_PRECONDITION vs UNIMPLEMENTED: 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. SendLiveMessage is a separate RPC.

Multiple client calls: Three rules (per mikeas1):

  1. No multi-connection support -> FAILED_PRECONDITION
  2. Multi-connection support -> treat as resubscribe with multiplexed input
  3. Agent has full authority over task lifecycle

Missing 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:

State Type States Stream behavior Resumable?
Interrupted INPUT_REQUIRED, AUTH_REQUIRED Stream stays open Yes, client sends new message on same stream
Terminal COMPLETED, FAILED, CANCELED, REJECTED Agent half-closes stream No

This 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:

Agent: TaskStatusUpdateEvent (state=INPUT_REQUIRED, message="What is your budget?")
  -- stream stays open, agent awaits inline --
Client: SendMessageRequest (new message with response)
Agent: TaskStatusUpdateEvent (state=WORKING)
  ... processing continues ...
Agent: TaskStatusUpdateEvent (state=COMPLETED) -> half-close

The agent can "await" the response inline without saving/reconstituting state. This is the primary advantage over SendStreamingMessage + separate SendMessage follow-up.

Timeout: Not specified in the protocol. Left to agent implementation, consistent with A2A's approach of leaving operational policies to implementations. Options:

  • Agent-side idle timeout -> transition to FAILED or CANCELED
  • Future extension: optional timeout field in SendMessageConfiguration

Multiple interruptions: Fully supported. Each interruption keeps the stream open:

Client: SendLiveMessage("book a flight to Paris")
Agent: WORKING
Agent: INPUT_REQUIRED ("What is your budget?")
Client: "$500"
Agent: WORKING
Agent: INPUT_REQUIRED ("Confirm date: March 15?")
Client: "Yes"
Agent: WORKING
Agent: COMPLETED (booking artifact) -> half-close

Error codes:

Code Condition
UNIMPLEMENTED Method not supported
NOT_FOUND Unknown task_id
FAILED_PRECONDITION Duplicate active stream
INVALID_ARGUMENT Malformed request

4. 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:

  1. 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.

  2. BiDi is NOT for multi-turn conversation. The recommended pattern for multi-turn remains:

    SendMessage -> Task A -> Complete
    SendMessage (reuse contextId) -> Task B -> Complete
    

    This is unchanged. BiDi doesn't replace or conflict with this pattern.

  3. BiDi IS for single-task human-in-the-loop. The specific use case is tasks that need clarification or approval mid-execution:

    SendLiveMessage -> Task -> WORKING -> INPUT_REQUIRED (approval needed)
    Client responds on same stream -> WORKING -> COMPLETED -> half-close
    

    Without BiDi, this requires: SendStreamingMessage -> stream closes on INPUT_REQUIRED -> new SendMessage -> new SendStreamingMessage to get updates. BiDi simplifies this to a single persistent connection.

  4. The "bad" pattern Tehsmash described isn't really bad for BiDi. The pattern Task -> input-required -> respond -> input-required -> respond -> complete is 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:

  • gRPC transport (BiDi is gRPC-only, not available over JSON-RPC/HTTP)
  • Agent declares capabilities.bidiStreaming: true
  • Task is likely to enter INPUT_REQUIRED or AUTH_REQUIRED states
  • Client wants to respond to interruptions without reconnecting

When NOT to Use SendLiveMessage

  • Simple request/response: Use SendMessage instead
  • One-way streaming (progress updates only): Use SendStreamingMessage
  • Multi-turn conversation (multiple tasks): Use separate SendMessage calls with shared contextId
  • Long-running background tasks: Use SendMessage + push notifications
  • Non-gRPC transports: BiDi is not available over JSON-RPC or HTTP+JSON

Pattern 1: Single-Task with Human Approval (Recommended BiDi Use Case)

A task that requires one or more human confirmations before completing.

┌─────────┐                              ┌─────────┐
│  Client  │                              │  Agent   │
└────┬─────┘                              └────┬─────┘
     │  SendLiveMessage(stream)                │
     │  "Book a flight to Paris"               │
     ├────────────────────────────────────────►│
     │                                         │
     │         Task (id=T1, SUBMITTED)         │
     │◄────────────────────────────────────────┤
     │     StatusUpdate (WORKING)              │
     │◄────────────────────────────────────────┤
     │     StatusUpdate (INPUT_REQUIRED)       │
     │     "Budget? Economy or business?"      │
     │◄────────────────────────────────────────┤
     │                                         │
     │  -- stream stays open --                │
     │                                         │
     │  "Economy, under $500"                  │
     ├────────────────────────────────────────►│
     │                                         │
     │     StatusUpdate (WORKING)              │
     │◄────────────────────────────────────────┤
     │     StatusUpdate (INPUT_REQUIRED)       │
     │     "Found LAX->HEL $450. Confirm?"    │
     │◄────────────────────────────────────────┤
     │                                         │
     │  "Yes, book it"                         │
     ├────────────────────────────────────────►│
     │                                         │
     │     StatusUpdate (WORKING)              │
     │◄────────────────────────────────────────┤
     │     ArtifactUpdate (booking receipt)    │
     │◄────────────────────────────────────────┤
     │     StatusUpdate (COMPLETED)            │
     │◄────────────────────────────────────────┤
     │         --- half-close ---              │

Pattern 2: Multi-Turn Conversation (Do NOT Use BiDi)

For multi-turn conversations, use separate RPC calls with shared contextId:

┌─────────┐                              ┌─────────┐
│  Client  │                              │  Agent   │
└────┬─────┘                              └────┬─────┘
     │  SendMessage (contextId=C1)             │
     │  "What flights go to Paris?"            │
     ├────────────────────────────────────────►│
     │  Task A (COMPLETED) - flight list       │
     │◄────────────────────────────────────────┤
     │                                         │
     │  SendMessage (contextId=C1)             │
     │  "Book the 2pm Air France one"          │
     ├────────────────────────────────────────►│
     │  Task B (COMPLETED) - booking           │
     │◄────────────────────────────────────────┤
     │                                         │
     │  SendMessage (contextId=C1)             │
     │  "Now find a hotel near CDG"            │
     ├────────────────────────────────────────►│
     │  Task C (COMPLETED) - hotel options     │
     │◄────────────────────────────────────────┤

Pattern 3: Reconnecting to an Active Task

A client can reconnect to an ongoing task's BiDi stream (e.g., after network disruption):

Client: SendLiveMessage(task_id=T1, empty parts)
  If T1 is active   -> Agent sends current state + begins updates
  If T1 is terminal  -> Agent sends final Task + half-close
  If T1 not found    -> NOT_FOUND error
  If T1 has existing stream and agent doesn't support multiple -> FAILED_PRECONDITION

Comparison Table

Scenario RPC Transport Stream closes on
Simple request/response SendMessage Any N/A (unary)
Progress updates SendStreamingMessage Any Terminal OR interrupted
Human-in-the-loop task SendLiveMessage gRPC only Terminal only
Background task SendMessage + push notifications Any N/A
Multi-turn conversation Multiple SendMessage calls Any N/A

Changes Made

Proto (specification/a2a.proto)

  • Added SendLiveMessage bidirectional streaming RPC after SendStreamingMessage
  • Added optional bool bidi_streaming = 5 to AgentCapabilities
  • Removed re-created legacy files (specification/grpc/a2a.proto, specification/json/a2a.json, types/src/types.ts)

Specification (docs/specification.md)

  • Added Section 3.1.3: Send Live Message operation (inputs, outputs, errors, behavior, config semantics, reconnection)
  • Added bidiStreaming capability validation bullet in Section 3.3.4
  • Added SendLiveMessage row to method mapping table in Section 5.3 (gRPC only, N/A for JSON-RPC and REST)
  • Added gRPC method subsection 10.4.3 for SendLiveMessage
  • Updated Section 10.7 (gRPC Streaming) to cover both server-streaming and bidirectional-streaming
  • Added SendLiveMessage to operations layer Mermaid diagram

Topic 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 use
  • docs/topics/key-concepts.md: Added bidirectional streaming as 4th interaction mechanism

Reference Docs

  • docs/llms.txt: Added SendLiveMessage to RPC methods table, bidi_streaming to capabilities list
  • docs/whats-new-v1.md: Added bidiStreaming to AgentCapabilities changes, added "NEW: SendLiveMessage" operation section

Bug Fix

  • docs/topics/extensions.md: Fixed broken snippet include — types/src/types.ts:AgentExtension (deleted file) replaced with specification/a2a.proto:AgentExtension

Action Items

  • Fix field ordering in AgentCapabilities
  • Apply changes to specification/a2a.proto (new file location)
  • Remove re-created legacy files
  • Add documentation on recommended BiDi streaming patterns
  • Update specification.md (operation, capability validation, method mapping, gRPC section)
  • Update streaming-and-async.md, key-concepts.md, llms.txt, whats-new-v1.md
  • Fix broken extensions.md snippet include
  • Request TSC review for merge
  • Consider specifying timeout behavior

mikeas1 and others added 5 commits February 26, 2026 11:52
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]>
@HungYangChang HungYangChang requested review from a team as code owners February 26, 2026 17:31
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @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 SendLiveMessage gRPC RPC. This change fundamentally improves how clients interact with agents for tasks requiring human intervention, such as approvals or clarifications. Instead of closing the stream and requiring a new request for each interruption, clients can now maintain a persistent connection, sending follow-up messages inline. This streamlines interactive workflows, reduces client-side state management, and clarifies the appropriate use cases for various streaming mechanisms within the A2A ecosystem.

Highlights

  • New Bidirectional Streaming RPC: Introduced SendLiveMessage, a new bidirectional streaming gRPC RPC, to enable interactive, human-in-the-loop agent interactions.
  • Persistent Stream for Interactive Tasks: Updated the A2A protocol to allow clients to maintain an active stream with agents, facilitating inline responses to INPUT_REQUIRED or AUTH_REQUIRED states without needing to reconnect.
  • Agent Capability Declaration: Added bidi_streaming capability to AgentCapabilities in a2a.proto and updated documentation to reflect this new feature.
  • Clarified Streaming Behaviors: Clarified the distinct behaviors of SendLiveMessage compared to SendStreamingMessage, particularly regarding stream closure on interrupted versus terminal states.
  • Comprehensive Documentation: Provided comprehensive documentation on recommended patterns, use cases, and anti-patterns for bidirectional streaming across various documentation files.
  • Documentation Bug Fix: Fixed a broken snippet include in extensions.md by updating the file path.
Changelog
  • docs/llms.txt
    • Added bidi_streaming to the list of agent capabilities.
    • Included SendLiveMessage in the RPC methods table.
  • docs/specification.md
    • Updated the A2A Operations Mermaid diagram to include "Live Message".
    • Added a new section (3.1.3) detailing the SendLiveMessage operation, including inputs, outputs, errors, behavior, configuration semantics, and reconnection.
    • Updated capability validation (3.3.4) to include bidiStreaming for SendLiveMessage.
    • Added SendLiveMessage to the method mapping table (5.3), noting it's gRPC-only.
    • Added a new gRPC method subsection (10.4.3) for SendLiveMessage with request, response, and error codes.
    • Updated the gRPC Streaming section (10.7) to cover both server-streaming and bidirectional-streaming.
  • docs/topics/extensions.md
    • Corrected the snippet include path for AgentExtension from types/src/types.ts to specification/a2a.proto.
  • docs/topics/key-concepts.md
    • Added "Bidirectional Streaming (gRPC)" as a new interaction mechanism.
  • docs/topics/streaming-and-async.md
    • Introduced a new section "Bidirectional Streaming with gRPC", detailing its purpose, key differences from SSE, how it works, and when to/not to use it.
    • Added a comparison table between SSE Streaming and BiDi Streaming.
    • Included protocol specification references for SendLiveMessage.
  • docs/whats-new-v1.md
    • Added a new section "NEW: SendLiveMessage (Bidirectional Streaming)" outlining changes from v0.3.0 to v1.0, including the new RPC, bidiStreaming capability, and behavior changes.
    • Added bidiStreaming to the "Added Fields" in the AgentCapabilities object section.
  • specification/a2a.proto
    • Defined the new rpc SendLiveMessage(stream SendMessageRequest) returns (stream StreamResponse) with detailed comments explaining its behavior, configuration, ID semantics, reconnection, and error handling.
    • Added optional bool bidi_streaming = 5; to the AgentCapabilities message.
Activity
  • gemini-code-assist (bot) provided feedback on field ordering and the distinction between FAILED_PRECONDITION and UNIMPLEMENTED error codes.
  • ToddSegal inquired about resubscribe interactions, handling multiple client calls, and the interpretation of missing task_id.
  • HungYangChang (author) clarified the differences between interrupted and terminal states, explained state transitions on BiDi streams, discussed timeout considerations, and detailed error codes.
  • Tehsmash raised a concern that bidirectional streaming might encourage multi-turn conversations tied to a single task.
  • HungYangChang (author) addressed Tehsmash's concern by clarifying that BiDi streaming is for single-task human-in-the-loop interactions, not multi-task conversational flows, and explained the intended pattern.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
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 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]>
James Chang and others added 3 commits February 26, 2026 13:45
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]>
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