Python: add agent-framework-hosting-a2a channel#6306
Conversation
There was a problem hiding this comment.
Automated Code Review
Reviewers: 3 | Confidence: 83%
✓ Correctness
The
_exact_path_routeutility properly handles the Starlette trailing-slash redirect issue for mounted root routes. The Telegram_is_echo_payloadfunction correctly identifies mirrored user turns for silent notification delivery, and_sendalready accepts**extrakwargs. The Discord_commandstype change from set to tuple is a valid fix (set is not a Sequence). Thedelete_webhook_on_shutdownflag and the channel route refactoring (contributing Route('/') and relying on the host mount path) are consistent and well-tested. One minor documentation inconsistency remains in the ResponsesChannel class docstring.
✓ Security Reliability
The A2A channel implementation is well-structured and follows existing patterns. Two security concerns: (1) the A2A executor sends raw exception messages to clients, risking information disclosure of internal details; (2) the new
pushmethod in the Activity Protocol channel makes authenticated HTTP requests to a URL extracted from identity attributes without validating it against the channel's existing service-URL allow-list, creating a defense-in-depth gap.
✓ Test Coverage
The new hosting-a2a package has good coverage of the happy paths (non-streaming execute, streaming execute, content conversion, agent card construction, route contribution). However, several meaningful behavioral branches in
HostAgentExecutorlack tests: thecancelmethod, the error/failure path inexecute, and the non-streaming path whendeliver_responsereturns False. The activity protocol channel changes have excellent test coverage including edge cases for commands, push, identity, and the non-edit-channel buffer path.
Automated review by eavanvalkenburg's agents
| logger.exception("A2AChannel encountered an error during execution.") | ||
| await updater.update_status( | ||
| state=TaskState.TASK_STATE_FAILED, | ||
| message=updater.new_agent_message([Part(text=str(exc))]), |
There was a problem hiding this comment.
Security: str(exc) is sent directly to the A2A client. If an unexpected internal exception occurs (database error, file-system error, credential failure, etc.), the raw message may leak sensitive details like file paths, hostnames, or connection strings. The exception is already logged on line 147 via logger.exception. Consider returning a generic message to the client instead.
| message=updater.new_agent_message([Part(text=str(exc))]), | |
| message=updater.new_agent_message([Part(text="Internal error during execution")]), |
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| pytest.main([__file__, "-v"]) |
There was a problem hiding this comment.
Missing test coverage for HostAgentExecutor.cancel() (line 113 of _executor.py). This public method validates context_id and publishes a TASK_STATE_CANCELED event — consider adding a test similar to test_execute_requires_context_id that also verifies the cancel event is emitted.
Also missing: a test for the execute error/failure path. When self._ctx.run(request) raises an exception, the executor catches it and sets TASK_STATE_FAILED with the error message (lines 143–148 of _executor.py). A test with a _FakeContext whose run raises would verify this important behavior.
| assert request.channel == "a2a" | ||
| assert request.input == "hello" | ||
| assert request.session is not None | ||
| assert request.session.isolation_key == "conv-1" |
There was a problem hiding this comment.
The non-streaming _run path has an early return when deliver_response returns False (line 161 of _executor.py), meaning no working-state messages are published to A2A. This branch is untested — _FakeContext always uses deliver=True. Consider adding a test with _FakeContext(deliver=False, reply="ignored") and asserting that no TASK_STATE_WORKING status events appear in the queue.
2041dd3 to
e1820a4
Compare
Add a hosting channel that exposes the host target (agent or workflow) as a peer agent over the Agent-to-Agent (A2A) protocol (JSON-RPC plus a served agent card). Requests are handled by a host-routed HostAgentExecutor that drives the host pipeline (ChannelContext.run/ run_stream) instead of wrapping the target directly, so sessions, linking, and run/response hooks apply. Maps the A2A conversation/context id to a ChannelSession isolation key and the caller to a ChannelIdentity; streaming emits incremental task artifacts. Includes tests, README, and workspace registration. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
e1820a4 to
b79c359
Compare
Motivation and Context
Part of the hosting channels work tracked in #6265.
The host can already expose its target (an agent or workflow) over several channels (Activity protocol, Telegram, Discord, Teams, Responses). This adds a channel that exposes the same target as a peer agent over the Agent-to-Agent (A2A) protocol, so A2A clients can discover and call the hosted agent/workflow.
Description
Adds the
agent-framework-hosting-a2apackage providingA2AChannel:HostAgentExecutorthat drives the host pipeline (ChannelContext.run/run_stream) rather than wrapping the target directly, so sessions, cross-channel linking, and run/response hooks all apply.ChannelSessionisolation key and the caller to aChannelIdentity. Non-streaming runs publish reply messages as task status updates; streaming runs publish incremental task artifacts.Includes unit tests, a README, and workspace registration in the root
pyproject.toml/uv.lock.Contribution Checklist