What a stranger gets: real agents answer — streamed when the agent supports it — and a thread keeps its conversation across gateway restarts, with zero storage.
Acceptance
- Turns go through the official
a2aproject/a2a-go v2 SDK: SendMessage, streaming when the AgentCard allows it, clean final-message fallback when not; SSE reconnect and poll fallback; CancelTask on timeout or abandon.
- Stateless continuity: the A2A
contextId is derived, never stored — a UUID-shaped HMAC-SHA256 keyed by A4H_IDENTITY_SECRET over (agent handle + conversation key). A2A v1.0 §3.4.1 permits client-provided contextIds and forbids conformant servers from silently replacing them.
- The guard: if a server responds with a different
contextId, adopt it in memory for that thread and log once. An unset secret generates a random per-process key plus one startup warning (continuity will not survive restarts).
- Deadlines on all I/O; wrapped errors; deterministic tests for the fallback ladder and the derivation (same thread ⇒ same id; different secret ⇒ different id).
- Reach the v0.3 installed base: the client is built through a2a-go's compat factory so agents still on A2A v0.3 (older ADK Python, kagent runtimes) work too — the wire format broke at v1.0, and this one option widens who can use a4h on day one.
- All three streaming shapes render correctly: artifact chunks (
append/lastChunk), repeated status-update messages, and a single streamed Message — real agents diverge; a fixture test per shape.
- Interrupted states handled per spec: a blocking send returns at
INPUT_REQUIRED/AUTH_REQUIRED (not an error); an AUTH_REQUIRED stream is kept open — it may self-resume; terminal tasks are immutable, so a thread follow-up always sends contextId only, never a finished task's id.
- Send hygiene:
acceptedOutputModes set from the adapter's capabilities; historyLength: 0 so servers do not echo history back; the card-declared tenant field is echoed when present (a v1.0 MUST).
Non-goals
Usage or cost extraction (A2A defines no field) · push notifications · retries beyond the reconnect ladder · in-flight turn recovery after a gateway restart (v0.2: ListTasks + resubscribe, zero storage — parked in the plan).
What a stranger gets: real agents answer — streamed when the agent supports it — and a thread keeps its conversation across gateway restarts, with zero storage.
Acceptance
a2aproject/a2a-gov2 SDK:SendMessage, streaming when the AgentCard allows it, clean final-message fallback when not; SSE reconnect and poll fallback;CancelTaskon timeout or abandon.contextIdis derived, never stored — a UUID-shaped HMAC-SHA256 keyed byA4H_IDENTITY_SECRETover (agent handle + conversation key). A2A v1.0 §3.4.1 permits client-provided contextIds and forbids conformant servers from silently replacing them.contextId, adopt it in memory for that thread and log once. An unset secret generates a random per-process key plus one startup warning (continuity will not survive restarts).append/lastChunk), repeated status-update messages, and a single streamed Message — real agents diverge; a fixture test per shape.INPUT_REQUIRED/AUTH_REQUIRED(not an error); anAUTH_REQUIREDstream is kept open — it may self-resume; terminal tasks are immutable, so a thread follow-up always sendscontextIdonly, never a finished task's id.acceptedOutputModesset from the adapter's capabilities;historyLength: 0so servers do not echo history back; the card-declared tenant field is echoed when present (a v1.0 MUST).Non-goals
Usage or cost extraction (A2A defines no field) · push notifications · retries beyond the reconnect ladder · in-flight turn recovery after a gateway restart (v0.2:
ListTasks+ resubscribe, zero storage — parked in the plan).