Skip to content

S2 — A2A client: streaming, fallback, and stateless thread continuity #14

Description

@fmind

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

  1. 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.
  2. 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.
  3. 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).
  4. Deadlines on all I/O; wrapped errors; deterministic tests for the fallback ladder and the derivation (same thread ⇒ same id; different secret ⇒ different id).
  5. 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.
  6. 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.
  7. 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.
  8. 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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions