Expand description
A2A (agent-to-agent) protocol support (DW-114, AI-12 #252).
This module implements the A2A protocol’s task lifecycle state
machine and the v0.3 JSON-RPC wire format against the A2A Protocol
Specification (https://github.com/a2aproject/A2A) as of 2025-Q4.
The task state vocabulary (submitted, working, completed,
failed, canceled) and the legal transitions are stable; the
wire format (JSON-RPC method names, envelope shapes) is the v0.3
shape (message/send, message/stream, tasks/get, etc.).
§What is implemented
- The
TaskStateMachinestruct: a validating state machine for A2A task lifecycle transitions. Legal transitions:Submitted -> Working(the agent starts processing)Submitted -> Failed(the agent rejects the task)Submitted -> Canceled(the caller cancels before processing)Working -> Completed(the agent finishes successfully)Working -> Failed(the agent fails mid-processing)Working -> Canceled(the caller cancels during processing)Completed,Failed,Canceledare terminal (no transitions out). Illegal transitions return anA2AErrornaming the attempted and target states.
- The
A2AAdapterstruct implementingProviderAdapter: it translates a canonicalChatRequestinto an A2A v0.3 JSON-RPCmessage/send(ormessage/streamfor streaming) body (A2AAdapter::build_request) and parses an A2A response back into the canonicalChatResponse(A2AAdapter::parse_response). The adapter handles both directMessageresults (withparts[]using thekinddiscriminator) andTaskresults (extracting text fromstatus.messageorartifacts). Error and stream-event parsing are wired too (the SSE framing reusescrate::ai::sse); stream events coverTaskStatusUpdateEvent,TaskArtifactUpdateEvent, and directMessageframes. - The
AgentCardstruct andAgentCardParser: parse the JSON-LD-ish Agent Card discovery doc (name, description, url, version, capabilities, authentication) from an inline JSON value or a file path. - The
TaskLifecycleenum: the task state vocabulary (Submitted,Working,Completed,Failed,Canceled). - The
A2ASessionstruct: reuses the MCP session-management patterns (session id, TTL, max-concurrent) for agent-to-agent task sessions. Each session owns aTaskStateMachine. - The
handle_a2a_requestfunction: routes an A2A call through the existingdataplane::ai_proxypath (the transport is the agent’s named upstream, exactly like a regular provider).
§v0.3 wire format (AI-12, #252)
The adapter speaks the A2A v0.3 JSON-RPC binding:
- Request:
message/send(non-streaming) ormessage/stream(streaming). The canonicalChatRequestis folded intoparams.message.parts[]using thekind-discriminated part shape (text,file,data). System messages are folded as a text preamble (A2A has no system role). The message carries a generatedmessageIdandrole: "user". - Response: the adapter detects whether the JSON-RPC
resultis aMessage(haspartsorkind: "message") or aTask(hasstatusorkind: "task"). For Tasks, text is extracted fromstatus.message.partsorartifacts[].parts. The task state maps toFinishReason(completed->Stop,failed->Other("failed"), etc.). - Streaming: SSE frames carry JSON-RPC responses whose
resultis one ofTaskStatusUpdateEvent(hasstatus),TaskArtifactUpdateEvent(hasartifact), or a directMessage(hasparts). Terminal states emit a finish delta. - Backward compat: the adapter still accepts the older
content/typemessage shape and theresult.messagewrapper for agents that have not migrated to v0.3.
§Dependency direction
ai depends on config only. The A2A adapter is a pure translator
(no HTTP client of its own), mirroring the OpenAI/Anthropic/Gemini
adapters and the MCP gateway. No new dependencies are introduced
(the scaffold is hand-rolled, the same locked M4 decision as MCP).
§Feature gate
The a2a cargo feature is flag-only (no new deps). When it is OFF,
the ai.a2a config block is accepted but inert: validation warns,
and CompiledA2a::compile returns None (no A2A providers are
wired into the alias table). When it is ON, each configured agent
appears as a provider of kind a2a in the model alias table.
Structs§
- A2AAdapter
- The A2A provider adapter (DW-114, DP-12 #252). A pure translator,
like the OpenAI/Anthropic/Gemini adapters: it holds no state and
opens no connections. The transport is the agent’s named upstream,
driven from
dataplane::ai_proxy. - A2ASession
- One A2A task session (DW-114), mirroring MCP’s session model: a
session id, a TTL, and a max-concurrent cap. The session is a
correlation handle for an agent-to-agent task exchange and owns a
TaskStateMachinetracking the task’s lifecycle state. - A2AStub
- The former stub error type, kept as a compatibility alias for the
handle_a2a_requestfunction (which is about the network call, not the state machine). New code should useA2AErrordirectly. - Agent
Card - An Agent Card (DW-114): the JSON-LD-ish discovery doc an A2A agent
publishes to declare its identity, capabilities, and
authentication. Parsed from an inline JSON value or a file path by
AgentCardParser. - Agent
Card Parser - Parses Agent Card JSON (DW-114) from an inline JSON value or a
file path. The card shape is JSON-LD-ish: a top-level object with
name,description,url,version,capabilities, andauthenticationfields. Unknown fields are tolerated (the spec is not frozen). Required fields arenameandurl; missing either is a parse error. - Compiled
A2a - The compiled A2A surface (DW-114): the agent table and session
policy. Built at
AiRuntimecompile time from theai.a2aconfig block; immutable once built. None when the block is absent or thea2afeature is off (the block is inert). - Compiled
A2aAgent - One compiled A2A agent (DW-114): the resolved provider entry an
alias can route to. Built at
AiRuntimecompile time from theai.a2a.agents[]config; immutable once built. - Task
State Machine - A validating state machine for A2A task lifecycle transitions
(DW-114). Holds the current state; transition methods validate the
transition and return the new state, or an
A2AErrorwhen the transition is illegal.
Enums§
- A2AError
- Error returned by A2A task lifecycle operations. Replaces the
former
A2AStub(the spec is now sufficiently stable for the task state machine; the wire format may still evolve). TheA2AStubtype is kept as an alias for backward compatibility with thehandle_a2a_requestfunction (which is about the network call, not the state machine). - Task
Lifecycle - The A2A task lifecycle state vocabulary (DW-114). The states follow
the A2A Protocol Specification draft:
submitted,working,completed,failed,canceled. The legal transitions between them are enforced byTaskStateMachine.
Functions§
- a2a_
providers - The A2A agent entries that should appear as providers in the alias
table (DW-114). Each entry is
(name, upstream)— the provider name and the upstream that carries its transport. TheAiRuntimecompile path inserts these into the provider pool withkind: a2a. Returns an empty vec when the block is absent or inert. - compile_
a2a - Build the
CompiledA2afrom the gateway’sai.a2ablock, for theAiRuntimecompile path. Returns None when the block is absent or inert (feature off / disabled). - handle_
a2a_ request - Route an A2A call through the existing
dataplane::ai_proxypath (DW-114). The transport is the agent’s named upstream, exactly like a regular provider: theA2AAdapterbuilds the task-submit request,ai_proxyplaces the call through the upstream, and the response is parsed back to the canonical shape. This function is the seam the dataplane calls for an A2A-routed alias; today it returns anA2AStuberror because the adapter intentionally does not own an HTTP client (the transport is the agent’s upstream, driven fromdataplane::ai_proxy). The task lifecycle state machine itself is implemented (seeTaskStateMachine); this function is the network-call seam, not the state machine.