feat(a2a): initial implementation#160
Conversation
|
Promptless prepared a documentation update related to this change. Triggered by mezmo/aura#160 This PR adds A2A protocol support but the README.md doesn't mention this capability. I've drafted a docs update that adds A2A to the key capabilities list, includes API examples for agent card discovery and messaging, and links to the detailed implementation docs. Review: Document A2A protocol support |
1fceb93 to
7619961
Compare
justintime4tea
left a comment
There was a problem hiding this comment.
I'm still working through this a bit but wanted to relay some initial feedback.
877d04f to
62ec511
Compare
This comment was marked as resolved.
This comment was marked as resolved.
66cf9c3 to
b0a4233
Compare
b0a4233 to
b706482
Compare
summary: codes A2A processing against the official a2a project rust implementation. An official crate has yet to be released, so directly imports the github projects pinned to the latest version as of 13 May 2026. Currently handles task generation calling internal aura SSE processing. Uses an in-memory store to keep dependencies down. ref: LOG-23822
The cancel hook cancels a token that nothing observes:
`Agent::stream(...)` accepts the token as `_cancel_token` and ignores
it, and the real MCP cancellation path is keyed off `request_id` and
triggered by an explicit `agent.cancel_and_close_mcp(request_id,
reason)` — which the executor never called from either the cancel
hook or the server-shutdown post-loop. Result: CancelTask returned
`Canceled` but the agent kept streaming, in-flight MCP tool calls on
remote servers never received `notifications/cancelled` (whether the
trigger was a client cancel or a graceful shutdown), and the trailing
`if success { yield Completed }` block could clobber the Canceled
state back to Completed. The executor also hand-rolled
increment/decrement of ActiveRequestTracker in two places, which
could double-decrement (or decrement before increment on a fast
cancel) and underflow the counter, breaking shutdown drain.
Cancellation propagation:
- Register per-task `request_id` with `aura::RequestCancellation` for
parity with the OpenAI handler, and unregister on every exit path.
- Stash an `Arc<dyn StreamingAgent>` in the per-task cancellation
entry so `cancel()` can call `agent.cancel_and_close_mcp(...)` to
send `notifications/cancelled` to in-flight MCP tool calls.
- Wrap `stream.next()` in `tokio::select!` against
`cancel_token.cancelled()` so the executor stops emitting artifacts
the moment cancel fires.
- Gate the final `Completed` yield on `!cancel_token.is_cancelled()`
so a cancel that lands while the stream is producing its final
chunk doesn't get overwritten.
- Drive MCP cleanup on server shutdown. `cancel_token` is a child of
`stream_shutdown_token`, so shutdown wakes the select! and breaks
the loop — but nothing was notifying MCP. The post-loop now
distinguishes the two cancel sources by checking whether the
cancel-map entry is still present (cancel() removes its entry
before firing the token, so a still-present entry means the
parent shutdown token fired), calls
`agent.cancel_and_close_mcp(request_id, "server shutdown")` in
that branch, and yields a terminal `Canceled` status so the
client sees a clean end instead of a silent stream close.
Mirrors the OpenAI handler's `StreamTermination::Shutdown` arm.
Active-request tracking (closes underflow race):
- Extract `ActiveRequestGuard` from `handlers.rs` (private) to
`types.rs` (`pub`) so both handlers can share it. No behavior
change on the OpenAI side — the local duplicate is removed in
favor of the shared type.
- Replace the manual `active_request_tracker.increment()` /
`.decrement()` pairs in the A2A executor with the RAII guard.
Drop on generator exit (loop break, early return, panic, consumer
drop) produces exactly one decrement, regardless of which side
initiates termination.
- `cancel()` no longer touches the tracker — it only fires the token
and cleans up the cancel-map entry. The in-flight execute() loop
catches the cancel via select!, breaks, drops the guard,
decrements once.
Test:
- Extend `test_cancel_task` to poll `GetTask` until the task quiesces
(same state + artifact count across two reads). State is asserted
`Canceled` on every poll, so a Completed-clobber is caught the
instant it happens; an executor that keeps emitting past cancel
never stabilizes and trips the 10s deadline.
Ref: LOG-23822
summary: bumping the a2a-rs package so that included headers are indeed forwarded to aura as expected. ref: LOG-23822
b706482 to
bfef649
Compare
|
I've spent some time with the code but found the tooling for manually testing A2A to be a bit lacking. https://github.com/a2aproject/a2a-rs has a basic CLI bundled for trying these things that I'll vet in a few for better verification. I've also looked at putting together with a claude a light internal tool on top to get a bit better visibility as the CLI is a bit thin in functionality. |
|
While testing with Here's a PR that should resolve the issue and make the A2A impl work with a2acli and other A2A clients: Something else I spotted, when you do connect using
|
|
Can we cargo-feature-gate A2A so that AURA Web Server can be built without A2A? There may be deployments where people don't want to serve A2A at all and I feel it should be an "opt-in". We can include it in the docker image for AURA but people should be able to build/deploy AURA without A2A. |
A2A clients read the agent card's interface URLs and pass them straight to their HTTP layer, which rejects relative paths. The card advertised "/a2a/v1" and "/a2a/v1/rpc", so message/send failed with a reqwest "builder error" while the card fetch worked. Build the interface URLs from a canonical base. Add the --server-url flag (AURA_SERVER_URL), falling back to host/port with 0.0.0.0 mapped to 127.0.0.1. Re-enable the integration-a2a cfg gate on a2a_test (it was commented out, leaking the card test into suites with a different agent name) and update its expected URLs to absolute. Wire AURA_SERVER_URL per topology in compose so the server publishes the address the test runner reaches it on: aura-web-server:8080 in CI, localhost:8080 for local dev. Ref: LOG-23822
Non-issue. Tokens are only included if provided from aura itself in the final events and when tokens are used. That prompt has no bones. |
summary: there are potentially some hosts of aura which do not prefer to expose A2A endpoints. Add new --enable-a2a flag so its an opt-in feature ref: LOG-23822
cfe2159 to
a324d3e
Compare
justintime4tea
left a comment
There was a problem hiding this comment.
Looking good @dominic-mcallister-logdna!
@Shearerbeard did you get a chance to run this through a2acli and your A2A inspection tool you mentioned?
Not yet, I'll see if I can between flights in a few. |

summary: codes A2A processing against the official a2a project rust implementation. An official crate has yet to be released, so directly imports the Github projects pinned to the latest version as of 13 May 2026. Currently handles task generation
calling internal aura SSE processing. Uses an in-memory store to keep dependencies down.
ref: LOG-23822