Skip to content

feat: Talk to agents over A2A (agent card / send / task) - #33

Merged
a1594834522-coder merged 2 commits into
mainfrom
feat/a2a
Sep 14, 2026
Merged

a1594834522-coder merged 2 commits into
mainfrom
feat/a2a

Conversation

@a1594834522-coder

Copy link
Copy Markdown
Contributor

What

Binds the A2A protocol endpoints in the open-api to the CLI, under the existing agent tree:

memorylake agent card <agent>
memorylake agent send <agent> --text … [--context] [--task] [--stream|--no-wait] [--raw]
                                 [--actor] [--project] [--read-only-project]… [--skip-memory] [--metadata-json]
memorylake agent task list|get|cancel|feedback <agent> …

All take --workspace with the same default as agent bind.

Design decisions

  • A2A v1.0 HTTP+JSON only. The unversioned /a2a/... paths are v1.0; the /a2a/v1/ paths are protocol 0.3 (confirmed by the agent card's supportedInterfaces). JSON-RPC and the 0.3 REST surface are not bound.
  • Responses are serde_json::Value. The API documents them without a schema; success bodies are bare A2A documents, not envelopes. Errors still arrive as envelopes, and describe_a2a_error lifts the A2A message + reason out of the escaped document so task cancel on a finished task says Task cannot be canceled - current state: 3 (TASK_NOT_CANCELABLE) rather than a blob of \".
  • send blocks by default (measured: production returns TASK_STATE_COMPLETED with artifacts in a few seconds). Reply text → stdout, task … context … state … → stderr, so scripts can pipe the reply and still continue the thread. TASK_STATE_INPUT_REQUIRED prints how to answer.
  • --stream reads the SSE body with a small parser in core::sse. Production streams the reply token-by-token in statusUpdate events and then once more in full as an artifactUpdate; the artifact is printed only when nothing was streamed.
  • A2A-Extensions: extensions://task-feedback/v1 is sent on task get, task list and task feedback. Measured: feedback records without the header, but task get only shows metadata."task-feedback/v1" with it.
  • --actor is not defaulted to actor me; production accepts an actor that is not bound to the workspace, but the server's own default is the right fallback.

Not in this PR

agent task subscribe (reattaching to a --no-wait task's stream), and flattened flags for overrides — those go through --metadata-json.

Tests

  • core: SSE parser, request serialization (camelCase, absent-when-empty), path constants, error rewrite, header/stream behaviour against the loopback server.
  • CLI wire: method/path/query/body/header for every subcommand; stream stub writes frames one at a time with pauses.
  • CLI offline: help, workspace required, content flags exclusive, --stream/--no-wait conflict, feedback validation, --page-size range.
  • CLI live: a2a_round_trip_against_the_default_agent — card → send (--skip-memory) → task get → list → feedback → cancel refused → stream prints once. Passes locally; full cargo test --workspace green apart from the known transport flakiness when live tests run in parallel.

🤖 Generated with Claude Code

a1594834522-coder and others added 2 commits September 14, 2026 17:26
Every agent bound to a workspace answers over the A2A protocol. This binds
the A2A v1.0 HTTP+JSON surface — the unversioned `/a2a/...` paths; the
`/a2a/v1/` paths are protocol 0.3 despite their name — and leaves the
JSON-RPC endpoint and the 0.3 REST surface alone.

Core gains `get_json_with_headers` / `post_json_with_headers` for endpoints
whose success body is a bare document rather than an envelope (errors still
arrive as envelopes and decode as before), `post_event_stream` plus an SSE
parser for `message:stream`, and `describe_a2a_error`, which lifts the A2A
message and reason out of the escaped document the envelope carries.

`agent send` blocks by default, prints the reply on stdout and the task /
context / state on stderr. `--stream` prints fragments as they land and
suppresses the closing artifact that repeats them; `--no-wait` sets
`returnImmediately`; `--raw` prints the protocol JSON. `--actor`, `--project`,
`--read-only-project`, `--skip-memory` map onto `metadata.memorylake`, with
`--metadata-json` for the rest. `agent task get|feedback` send the
`A2A-Extensions` header, without which the server hides ratings.

Measured against production: default send is blocking, feedback records
without the header but only reads back with it, a permission failure on the
stream endpoint comes as a JSON envelope, and cancelling a finished task
answers `TASK_NOT_CANCELABLE`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The CI account carries hundreds of scratch workspaces, so the default
workspace is not on the first page there.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@a1594834522-coder
a1594834522-coder merged commit 31f9638 into main Sep 14, 2026
6 checks passed
@a1594834522-coder
a1594834522-coder deleted the feat/a2a branch September 14, 2026 09:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant