Skip to content

Message Protocol ​

Every interaction between an agent and its environment — tool calls, memory operations, skill execution, multi-agent delegation — travels over a single, typed, versioned wire format. This NDJSON protocol is the contract that decouples agents from backends: as long as both sides speak A2E messages, the implementations can evolve independently.

Wire Format ​

A2E uses NDJSON (newline-delimited JSON) as its wire format. Each line is a self-contained JSON object representing one message. Compact JSON encoding (no extra whitespace) is used for efficiency.

{"a2e":"1.0","id":"a1b2c3","type":"tool/call/req","ts":1716123456.789,...}\n
{"a2e":"1.0","id":"d4e5f6","type":"tool/call/resp","ts":1716123457.012,...}\n

A2EMessage Base ​

Every A2E message inherits from A2EMessage (Pydantic BaseModel):

FieldTypeDescription
typestrMessage type identifier (e.g. "tool/call/req")
idstrUUID hex, auto-generated per message
versionstrProtocol version, always "1.0"
tsfloatUnix epoch timestamp

Methods: to_dict() (serialize), new_id() (regenerate UUID).

Protocol Versioning ​

Protocol version is "1.0", described as a strict superset of SCP 1.0 (Skill Call Protocol). Unknown message types degrade gracefully — the base type map covers SCP-compatible handshake/ping/pong/shutdown/error, and any unrecognized type falls back to a generic A2EMessage.

Message Lifecycle ​

Correlation ​

RPC requests are correlated by req_id — the client generates a UUID, the server copies it into the response. The client maintains a dict of req_id -> queue.Queue, so multiple concurrent RPCs can be in flight. Events reference their parent request via req_id.

Event Streaming ​

A2EEvent (type: invoke/event) provides streaming mid-operation updates:

FieldTypeDescription
kindEventKindprogress, artifact, log, or status
req_idstrCorrelates to the originating request
datadictPayload (progress %, artifact content, log text, status message)
seqintMonotonic sequence number within the request

Events are delivered before the final response, enabling progressive UI updates.

Capability Data Lifecycles ​

Most message types are RPC request/response pairs correlated by req_id. Some capabilities additionally expose an orthogonal data lifecycle — a set of messages that move task data but never carry the bytes themselves. The canonical example is the env data plane (env/data/{reset,add,get}):

  • Requests carry metadata only — paths, checksums, sizes, content types.
  • Bytes are moved by the host on its own filesystem (bind-mounted paths, archive extraction), so multi-GB corpora never traverse the protocol.
  • The data lifecycle is independent of the operation lifecycle it belongs to: an episode can be reset without touching data, and the data root restored without starting an episode.

This keeps the wire contract small and stable while letting hosts stage arbitrarily large inputs and collect arbitrarily large agent artifacts.

Error Handling ​

A2EError is returned when a message cannot be processed:

FieldTypeDescription
req_idstrThe failed request's ID
codeA2EErrorCodeMachine-readable error code
messagestrHuman-readable error description
detaildictAdditional structured context
retryableboolWhether the client should retry
capability_namestrWhich capability namespace failed

See Error Codes for the full list.

Type Registry ​

Both client and server maintain a type_registry mapping type strings to Pydantic model classes. The base map (A2E_BASE_TYPE_MAP) covers core types:

Type StringModel Class
handshake/reqHandshakeRequest
handshake/respHandshakeResponse
invoke/eventA2EEvent
pingPing
pongPong
shutdownShutdown
errorA2EError

Plugins extend this at startup by registering their own TYPE_MAP entries (e.g. tool/call/req -> ToolCallRequest).

A2E Protocol v1.0 — Released under the MIT License.