feat: A2A (Agent-to-Agent) protocol support#3567
Conversation
Add bidirectional A2A protocol integration for the OpenAI Agents SDK. Includes client tool, server agent, message converter, and AgentCard generator. A2A is an optional dependency ([a2a] extra). fix(a2a): address Codex review feedback - Export A2AServerAgent from extensions.a2a - Include default protocol binding in generated AgentCards - Honor timeout_seconds=None to disable timeout - Treat TASK_STATE_REJECTED as tool error - Publish A2A lifecycle events through the agent event queue Co-Authored-By: Oxygen56 <100782273+Oxygen56@users.noreply.github.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e1f0d2d226
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| AgentInterface( | ||
| url=url, | ||
| protocol_binding="a2a-json-rpc", | ||
| protocol_version="1.0", |
There was a problem hiding this comment.
Use the standard JSONRPC binding
When supported_interfaces is omitted, every generated AgentCard advertises a2a-json-rpc, but A2A 1.0 transport matching uses the standard binding strings such as JSONRPC, GRPC, and HTTP+JSON. Agents exposed with this helper will therefore publish cards that the default ClientFactory cannot match, making the generated server card unusable by standard A2A clients unless callers manually override the interface.
Useful? React with 👍 / 👎.
| if self.run_config is not None and getattr( | ||
| self.run_config, "streaming_enabled", False | ||
| ): |
There was a problem hiding this comment.
Enable streaming from request context, not RunConfig
This branch is effectively unreachable for normal SDK usage because RunConfig does not define a streaming_enabled field, so both message/send and message/stream executions fall through to Runner.run(). As a result the server advertises streaming support but streaming clients only receive the initial working update and final task, not the lifecycle events produced by Runner.run_streamed().
Useful? React with 👍 / 👎.
| new_items = getattr(result, "new_items", []) | ||
| if new_items: | ||
| existing, _ = self._sessions.get(context_id, ([], time.monotonic())) | ||
| existing.extend(new_items) | ||
| self._sessions[context_id] = (existing, time.monotonic()) |
There was a problem hiding this comment.
Persist model input items instead of RunItem objects
On a second request with the same context_id, _get_session() returns the raw RunItem instances stored here and execute() passes them directly into Runner.run() alongside the new A2A message. The runner expects response input dicts/Pydantic items rather than SDK RunItem wrappers, so real model calls can receive non-serializable history; convert each run item via to_input_item() before extending the session.
Useful? React with 👍 / 👎.
| except asyncio.CancelledError: | ||
| # Task was cancelled by the framework | ||
| failed_task = openai_error_to_failed_task( | ||
| asyncio.CancelledError("Task was cancelled."), | ||
| task_id=task_id, | ||
| context_id=context_id, | ||
| ) | ||
| await self._publish_task(event_queue, failed_task) |
There was a problem hiding this comment.
Publish cancellation as canceled, not failed
When cancel() cancels a running task, the cancelled execute() coroutine lands here and publishes openai_error_to_failed_task(), so clients can observe a final TASK_STATE_FAILED task after requesting cancellation. For cancellation flows this should emit a canceled task/status (or avoid overwriting the cancel status), otherwise successful cancels are reported as agent failures.
Useful? React with 👍 / 👎.
|
Thanks for sharing this. However, we don't have immediate plans to add this module within this SDK. If you need it for your projects and you're willing to maintain it as your own package, please feel free to publish such as your own project. |
Summary
Add bidirectional A2A (Agent-to-Agent) protocol integration for the OpenAI Agents SDK, enabling interoperability between OpenAI agents and any A2A-compatible agent built with any framework/language.
Closes #472.
What is included
Bidirectional Message Converter (
_converter.py)Message/Part/Task/Artifact↔ OpenAITResponseInputItem/RunResult/StreamEventA2A Client Tool (
_client_tool.py).well-known/agent-card.jsonA2A Server Agent (
_server_executor.py)AgentExecutorinterfacecontext_idAgentCard Generator (
_agent_card.py)Design decisions
a2a-sdkas optional dependency ([a2a]extra)__getattr__frozensetfor terminal statesTests
Breaking changes
None. This is a purely additive extension under
agents.extensions.a2a.