From 77504c9656cab31ff956b2dc34ab682616b2e89d Mon Sep 17 00:00:00 2001 From: Sampath Kumar Date: Tue, 4 Aug 2026 17:19:20 +0000 Subject: [PATCH 01/31] Add a2a-cli specification v0.1 draft Introduce the specification directory: the a2a-cli behavior specification, a compliance-report template, and a directory README. The specification defines what a command-line client must do to be conformant, so that independently built CLIs converge on one predictable command surface, output contract, and conversation model: - Tiered conformance (Core / Standard / Advanced), evidence-based through a published compliance report rather than self-declaration - Multi-turn conversation and session state: context/task id continuation, reporting identifiers back to the caller, and resumability - Task status polling as a required path alongside optional streaming - Opinionated defaults for a seamless experience, always overridable - Structured output contract, semantic exit codes, and a minimal JSON envelope - Authentication, card-driven transport selection, and version signaling - A single lightweight SKILL.md so AI coding agents can drive the tool Also expands the top-level README to point at the specification directory. Status: v0.1 draft, open for review. --- README.md | 19 +- specification/COMPLIANCE.md | 88 +++++++++ specification/README.md | 43 +++++ specification/SPEC.md | 370 ++++++++++++++++++++++++++++++++++++ 4 files changed, 519 insertions(+), 1 deletion(-) create mode 100644 specification/COMPLIANCE.md create mode 100644 specification/README.md create mode 100644 specification/SPEC.md diff --git a/README.md b/README.md index 67420cf..a7a4f79 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,19 @@ # a2a-cli -The official command-line interface for interacting with A2A (Agent-to-Agent) compatible agents. + +A command-line client for [A2A (Agent2Agent)](https://a2a-protocol.org) compatible agents — discover an agent, send it a message, and follow the resulting task from your terminal or from an AI coding agent. + +This repository holds the **specification** for `a2a-cli` and the compliance-report template used to demonstrate conformance. + +## Contents + +| Path | Description | +| --- | --- | +| [`specification/`](./specification) | The `a2a-cli` specification and compliance-report template. | + +## Status + +Early work in progress. The specification is a **v0.1 draft**, open for review — expect changes. Feedback via issues and pull requests is welcome. + +## License + +[Apache 2.0](./LICENSE) diff --git a/specification/COMPLIANCE.md b/specification/COMPLIANCE.md new file mode 100644 index 0000000..82f07f1 --- /dev/null +++ b/specification/COMPLIANCE.md @@ -0,0 +1,88 @@ +# a2a-cli Compliance Report + +> **Template.** Copy this file into your tool's repository, complete every field, and replace all `<…>` placeholders. Link the finished report from the A2A compatibility matrix. A tool MUST NOT advertise a tier it has not demonstrated here. + +## 1. Summary + +| Field | Value | +| --- | --- | +| Tool name | `` | +| Tool version | `` | +| Implementation language | `` | +| Repository | `` | +| Maintainer / contact | `` | +| Report date | `` | +| Specification version targeted | `0.1` | +| **Tier claimed** | `` | +| A2A protocol version(s) | `` | +| A2A TCK version(s) tested | `` | +| Transports covered | `` | +| Designation | `` | + +## 2. Legend + +`✅ Pass` · `◐ Partial` · `❌ Fail` · `— N/A`. Every `◐`/`❌`/`—` MUST carry a note. + +## 3. Tier 1 — Core (required) + +| # | Requirement | Spec § | Status | Notes | +| --- | --- | --- | --- | --- | +| 1 | `discover` — fetch & parse Agent Card | §8.1 | `<>` | | +| 2 | `send` — start/continue; blocking by default with `--async` override | §8.2, §4.5 | `<>` | | +| 3 | `send --stream` — SSE when supported; no hang when unsupported | §8.2, §7.2 | `<>` | | +| 4 | `get` — task state, artifacts, history | §8.3 | `<>` | | +| 5 | `cancel` — idempotent cancel | §8.4 | `<>` | | +| 6 | Continuation via `--context-id` / `--task-id` (no invented IDs; no silent new task) | §6.1, §6.2 | `<>` | | +| 7 | Reports `taskId` / `contextId` / `state` back on completion & interruption | §6.3 | `<>` | | +| 8 | Session persistence *(if state is persisted)* — conventional path, secrets not world-readable (`0600`), explicit flags override stored state | §6.4 | `<>` | | +| 9 | Polling path — `get` + wait/watch with `--poll-interval` / `--timeout` | §7.3 | `<>` | | +| 10 | Handles interrupted states (`INPUT_REQUIRED` / `AUTH_REQUIRED`) without deadlock | §7.1, §8.2 | `<>` | | +| 11 | Output contract — `--output json` on stdout, diagnostics on stderr, auto-degrade off-TTY | §9.1, §9.2 | `<>` | | +| 12 | Minimal JSON envelope (`taskId`/`contextId`/`state`, error object) | §9.3, Appendix B | `<>` | | +| 13 | Errors machine-readable **and normalized across transports** (same A2A error → same result) | §9.4 | `<>` | | +| 14 | Exit-code scheme (0–7) | §9.5 | `<>` | | +| 15 | Tier-1 auth — bearer / API key / custom header (scriptable) | §10.1 | `<>` | | +| 16 | Transport selection from the Agent Card; HTTP+JSON default | §11.1, §4.5 | `<>` | | +| 17 | `A2A-Version` signaled on every request (explicit; no silent downgrade) | §11.2 | `<>` | | +| 18 | Opinionated defaults, each overridable by flag | §4.5 | `<>` | | +| 19 | Ships exactly one lightweight, generic `SKILL.md`; spec & skill kept as distinct layers (skill does not restate normative requirements) | §12.1–§12.3 | `<>` | | + +## 4. Tier 2 — Standard + +| # | Requirement | Spec § | Status | Notes | +| --- | --- | --- | --- | --- | +| 1 | `list` — cursor-paginated, filter by status/context | §8.5 | `<>` | | +| 2 | `subscribe` — (re)subscribe / stream reconnect | §7.4, §8.5 | `<>` | | +| 3 | `auth login` — OAuth 2.1 device-code + client-credentials; secure token store | §10.2 | `<>` | | +| 4 | ≥ 2 transports selectable from the card | §11.1 | `<>` | | +| 5 | Config profiles / named environments (`--env`) | §6.4, §8.5 | `<>` | | +| 6 | Interactive `chat` — carries context/task across turns | §6.2, §8.5 | `<>` | | +| 7 | `download` — save artifacts | §8.5 | `<>` | | +| 8 | Wire debug (`--dump-wire`) | §8.5 | `<>` | | +| 9 | `conformance` — TCK smoke check | §8.5 | `<>` | | +| 10 | Shell completions | §8.5 | `<>` | | + +## 5. Tier 3 — Advanced + +| # | Requirement | Spec § | Status | Notes | +| --- | --- | --- | --- | --- | +| 1 | `push-config` CRUD + webhook receiver | §8.5 | `<>` | | +| 2 | gRPC transport | §11.1 | `<>` | | +| 3 | Authenticated extended Agent Card | §8.5, §10.4 | `<>` | | +| 4 | Agent Card signature verification | §8.5 | `<>` | | +| 5 | mTLS | §10.3 | `<>` | | +| 6 | OpenID Connect | §10.3 | `<>` | | +| 7 | `serve` / mock agent | §8.5 | `<>` | | +| 8 | Catalog / registry integration | §8.5 | `<>` | | +| 9 | Protocol extensions | §11.3 | `<>` | | + +## 6. Test evidence + +- **How the tool was exercised:** `` +- **Results / logs:** `` +- **Environment:** `` +- **Known gaps / caveats:** `` + +## 7. Attestation + +Reported by `` on ``. The tier claimed in §1 reflects the evidence above. diff --git a/specification/README.md b/specification/README.md new file mode 100644 index 0000000..d114eec --- /dev/null +++ b/specification/README.md @@ -0,0 +1,43 @@ +# a2a-cli Specification + +This directory holds the specification for **`a2a-cli`** — a command-line client for the [Agent2Agent (A2A) Protocol](https://a2a-protocol.org/latest/specification/) — together with the compliance-report template used to demonstrate conformance. + +`a2a-cli` lets developers and AI coding agents discover, message, stream, poll, and inspect A2A agents from the terminal. The specification defines the behavior a tool must exhibit so that independently built CLIs — in any language — converge on one predictable command surface, output contract, and conversation model, verifiable through a published compliance report. + +## Contents + +| File | Description | +| --- | --- | +| [`SPEC.md`](./SPEC.md) | The a2a-cli behavior specification (normative). | +| [`COMPLIANCE.md`](./COMPLIANCE.md) | A template a tool completes to declare and evidence its conformance. | + +## Conformance at a glance + +Conformance is tiered and cumulative; a tier is satisfied only when every MUST in it is satisfied. + +| Tier | Name | Summary | +| --- | --- | --- | +| **Tier 1** | Core | Discover, send, get, cancel; multi-turn conversation state; task polling; structured output + exit codes; token auth; version signaling; a `SKILL.md`. | +| **Tier 2** | Standard | Task listing, streaming subscribe, OAuth login, multiple transports, config profiles, interactive chat, artifact download, wire debug, conformance check, completions. | +| **Tier 3** | Advanced | Push notifications + webhook receiver, gRPC, extended card, signature verification, mTLS, OpenID Connect, serve/mock, catalog, extensions. | + +See [`SPEC.md`](./SPEC.md) for the normative detail. + +## Claiming conformance + +1. Implement a tier of the specification. +2. Exercise your tool against the A2A Technology Compatibility Kit (TCK) and `SPEC.md`. +3. Copy [`COMPLIANCE.md`](./COMPLIANCE.md) into your tool's repository and fill it in. +4. Submit it to be listed in the A2A **compatibility matrix**. + +Conformance is open to any implementation, in any language. "Official" is a separate, project-level designation (see `SPEC.md` §1.4 and §15); designating an official reference implementation does not restrict conformance. + +## Status + +**v0.1 — Draft.** Open for review; not yet ratified. Feedback is welcome via issues and pull requests. + +## References + +- A2A Protocol Specification v1.0 — https://a2a-protocol.org/latest/specification/ +- A2A Agent Discovery — https://a2a-protocol.org/latest/topics/agent-discovery/ +- A2A Streaming & Asynchronous Operations — https://a2a-protocol.org/latest/topics/streaming-and-async/ diff --git a/specification/SPEC.md b/specification/SPEC.md new file mode 100644 index 0000000..15877ca --- /dev/null +++ b/specification/SPEC.md @@ -0,0 +1,370 @@ +# a2a-cli Specification + +**Version:** 0.1 +**Status:** Draft — open for review. +**Applies to:** A2A Protocol v1.0 + +## Abstract + +This document specifies the behavior that a command-line interface (CLI) tool MUST, SHOULD, and MAY exhibit to be considered a conformant **`a2a-cli`** — a terminal client for the [Agent2Agent (A2A) Protocol](https://a2a-protocol.org/latest/specification/). It exists so that independently built CLIs, in any language, converge on one predictable command surface, output contract, and conversation model — measurable through a published compliance report. + +**Why this matters.** In the absence of an official command-line client, the developer experience of building, testing, and driving A2A agents through AI coding agents is prohibitively high — every team reinvents ad-hoc scripts. AI-native development needs a stable, scriptable A2A client that a coding agent can operate directly, across scenarios well beyond debugging. This specification defines that client. + +This is an **implementer's specification**. Its audience is engineers building or improving an `a2a-cli`. It constrains CLI behavior only and never modifies A2A wire semantics. + +## Notational conventions + +The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**, **SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **MAY**, and **OPTIONAL** are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119). + +References of the form "A2A §x" point to the A2A Protocol Specification v1.0. Where an A2A rule is load-bearing for the CLI it is restated here so this document is self-contained; the A2A specification remains authoritative for protocol semantics. + +--- + +## 1. Scope + +1.1 An `a2a-cli` is an **A2A client**: it initiates requests to an A2A server (a remote agent) and renders the responses. Acting *as* a server — publishing an Agent Card, generating server-side identifiers, or serving inbound requests — is **outside the baseline** of this specification (see §8.5, optional). + +1.2 The primary purpose of an `a2a-cli` is **conversation with an A2A server**. A2A conversations are multi-turn and stateful and typically span multiple CLI invocations. A conformant tool MUST therefore allow a caller to **start, continue, inspect, and resume** a conversation (§6), and MUST provide a **polling** path for task status in addition to any streaming support (§7). + +1.3 Conformance is **tiered and evidence-based** (§3). A tool asserts conformance by publishing a compliance report. Any number of **conformant** tools MAY coexist; conformance is open to any implementation, in any language, that passes the specification. + +1.4 **Conformant vs. official.** These are distinct: +- A **conformant** tool is any implementation that satisfies a tier of this specification and publishes a compliance report (§13). Conformance is open to all. +- An **official** tool is one the A2A project has designated as a project-maintained reference implementation, hosted under the A2A project's GitHub organization, demonstrated against the A2A TCK, and listed in the compatibility matrix (§13). "Official" denotes governance and demonstrated conformance — not exclusivity. Governance and the current official implementation(s) are defined in §15. + +--- + +## 2. Object model + +Restated from A2A §4 so this document stands alone: + +- **Message** — a single conversational turn. Has a `role` (`user` or `agent`) and one or more **Parts** (`text`, `file`, or `data`). Carries a client-assigned **`messageId`**. +- **Task** — the stateful unit of work a message may create. Identified by a server-assigned **`taskId`**; advances through a **TaskState** (§7.1); may emit **Artifacts**. +- **Artifact** — a task output (text, structured data, or file). Task outputs are delivered as Artifacts, not Messages; a conformant tool MUST render Artifacts. +- **`contextId`** — a server-assigned, opaque identifier that groups related tasks and messages into a single **conversation**. +- **AgentCard** — the server's machine-readable description of identity, capabilities, interfaces (transports), security schemes, and skills, obtained during discovery (§8.1). + +--- + +## 3. Conformance model + +3.1 A tool declares conformance **per tier**. A tier is satisfied only when **every MUST** in that tier is satisfied. Tiers are cumulative: Tier 2 requires Tier 1; Tier 3 requires Tier 2. + +| Tier | Name | Requirements | +| --- | --- | --- | +| **Tier 1** | Core | §4.5 default behavior · §6 conversation/session handling · §7 polling · §8.1–8.4 commands (`discover`, `send`, `get`, `cancel`) · §9 output & exit codes · §10.1 auth · §11 transport & versioning · §12 SKILL.md | +| **Tier 2** | Standard | Tier 1 + `list`, `subscribe`, OAuth `auth login`, ≥2 transports, config profiles, interactive `chat`, `download`, wire debug, `conformance`, shell completions | +| **Tier 3** | Advanced | Tier 2 + push-notification config CRUD and a webhook receiver, gRPC transport, authenticated extended Agent Card, Agent Card signature verification, mTLS, OpenID Connect, `serve`/mock mode, catalog/registry, extensions | + +3.2 Conformance MUST be demonstrated by a **compliance report** (§13) generated against the A2A Technology Compatibility Kit (TCK) and this specification. A tool MUST NOT advertise a tier it has not demonstrated. + +--- + +## 4. Design principles + +4.1 **Agent-first core, dual-mode.** The default behavioral contract MUST be safe for non-interactive and programmatic use (structured output, no interactive prompts required, deterministic exit codes). A rich interactive experience for humans MAY be layered on top and MUST be gated by terminal (TTY) detection. A conformant tool is a single dual-mode tool, not two separate tools. + +4.2 **Stable, versioned contract.** The `--output json` shape (§9, Appendix B) and the exit-code scheme (§9) are a stable contract; breaking changes require a specification version bump. + +4.3 **Explicit, recoverable state.** Every identifier needed to resume a conversation MUST be observable in output (§6.3). Conversation state MUST NOT exist only in process memory. + +4.4 **Transport- and language-agnostic.** Observable behavior MUST be identical across the JSON-RPC, HTTP+JSON, and gRPC bindings and across implementation languages. + +4.5 **Opinionated defaults, always overridable.** Good defaults are essential to a seamless experience: common tasks MUST work with minimal flags. A conformant tool MUST ship the baseline defaults below, and MUST make **every** default overridable — by an explicit flag at all times, and MAY additionally be settable via a config profile or environment variable. An explicit flag MUST take precedence over a configured default, which MUST take precedence over the built-in default. A tool SHOULD expose its effective defaults (e.g. via `--help`) so a user can see what will happen before overriding. + +| Behavior | Default | Override | +| --- | --- | --- | +| Transport | **HTTP+JSON**, when the Agent Card offers a choice or expresses no preference (subject to card-driven selection, §11.1) | `--transport ` | +| Task completion | **Wait** (block) until the task reaches a terminal or interrupted state | `--async` / `--return-immediately` (return the `taskId` immediately) | +| Output presentation | **Human-readable, minimal yet consistently structured** text (labeled fields; not raw JSON, not a verbose dashboard) | `--output `, `-n` (JSON/NDJSON) | +| Detail level | **Concise** | `-v, --verbose` for detailed output | +| Protocol version | The **latest** A2A version the tool supports, signaled explicitly (§11.2) | `--a2a-version ` | +| Transport security | **TLS verification enabled** | `--insecure` (development only; MUST warn) | + +--- + +## 5. Command surface & global options + +5.1 The command surface is `a2a-cli [arguments] [options]`. + +| Command | Tier | Purpose | +| --- | --- | --- | +| `discover` | 1 | Fetch and inspect an Agent Card | +| `send` | 1 | Send a message to start or continue a conversation | +| `get` | 1 | Retrieve a task's status and artifacts | +| `cancel` | 1 | Cancel an active task | +| `list` | 2 | List tasks | +| `subscribe` | 2 | (Re)subscribe to a task's event stream | +| `chat` | 2 | Interactive multi-turn session | +| `auth` | 2 | Interactive credential acquisition (OAuth) | +| `config` | 2 | Manage named environments/profiles | +| `download` | 2 | Save task artifacts | +| `conformance` | 2 | Smoke-check a live agent against the A2A TCK | +| `push-config` | 3 | Manage push-notification configurations | +| `serve` | 3 | Run a local mock agent (out of client baseline) | + +5.2 Global options (Tier 1 MUST unless noted): + +| Option | Meaning | +| --- | --- | +| `-u, --service-url ` | Target agent base URL (or `--env`). | +| `--context-id ` | Continue an existing conversation (§6.2). | +| `--task-id ` | Continue an existing task (§6.2). | +| `--continue` / `--last` | Resume the stored last conversation (§6.4). | +| `-o, --output ` | Output mode. Default: minimal, structured `text` (§4.5). `tui` is an opt-in interactive mode; `json` is machine-readable. | +| `-n` | Alias for `--output json` (NDJSON), non-interactive. | +| `--transport ` | Override the transport binding (default HTTP+JSON, §4.5), subject to the Agent Card (§11.1). | +| `--async` / `--return-immediately` | Do not wait; return the `taskId` immediately (default is to wait, §4.5 / §7.3). | +| `--wait` / `--watch` | Block until the task reaches a terminal or interrupted state. This is the default for `send` (§4.5); stating it explicitly overrides a configured default. On `get` it turns the one-shot read into a poll loop (§7.3). | +| `--poll-interval ` / `--timeout ` | Polling controls (§7.3). | +| `--bearer ` / `--api-key ` / `-H, --header ` | Credentials (§10.1). | +| `--a2a-version ` | Protocol version to signal (§11). | +| `--env ` | Named profile (Tier 2). | +| `-v, --verbose` | Detailed output; additional diagnostics to stderr. `--dump-wire` (Tier 2) emits raw protocol JSON. | +| `--insecure` | Disable TLS verification (development only; MUST emit a warning). | + +--- + +## 6. Conversation & session state + +A2A conversations span multiple invocations; the CLI is the bridge that carries state between them. + +### 6.1 Identifiers + +| Identifier | Assigned by | Role | Constraints | +| --- | --- | --- | --- | +| `messageId` | Client | Turn identity / idempotency | SHOULD be reused when retrying a turn, because Send is not guaranteed idempotent; reuse avoids duplicated work. | +| `taskId` | Server | Unit of work | A tool MUST NOT invent a `taskId` for a new task. A client-supplied `taskId` MUST reference an existing task; otherwise the server returns a not-found error. | +| `contextId` | Server | Conversation grouping | Opaque; a tool SHOULD NOT fabricate one. A `contextId` and `taskId` that do not correspond MUST be rejected by the server; a tool MUST NOT attempt to reconcile them. | + +A conformant tool never *creates* server identifiers; it **captures** them from responses and **replays** them on later turns. + +### 6.2 Continuing a conversation (MUST) + +A conformant tool MUST allow continuation via explicit options: + +- **`--context-id `** attaches this turn to an existing conversation (a new task within the same context). +- **`--task-id `** continues an existing task — for example, to respond to a task waiting in `INPUT_REQUIRED` (§7.1). + +Rules: +- When `--task-id` is supplied, the tool MUST send the message against that task and MUST surface any server error (e.g. not-found or state conflict) rather than silently starting a new task. +- When only `--context-id` is supplied, the tool starts a new task grouped under that conversation. +- When both are supplied, the tool MUST pass them through unchanged. +- Interactive `chat` (Tier 2) MUST carry the `contextId` — and the active `taskId` while a task is interrupted — across turns automatically. + +### 6.3 Reporting identifiers back (MUST) + +Because the next invocation depends on them, every command that touches a task MUST expose, on completion and on interruption: + +- the **`taskId`**, the **`contextId`**, and the current **task state**; +- in `--output json`, these MUST appear as stable top-level fields (`taskId`, `contextId`, `state`) per Appendix B; +- in human-facing modes, these MUST be printed in a copy-pasteable form, and the tool SHOULD print the exact command required to resume (for example, `a2a-cli send --task-id ""`). + +### 6.4 Local session state (SHOULD) + +- A tool SHOULD persist the most recent conversation (`contextId`, latest `taskId`, service URL) so a caller can resume without re-supplying identifiers (e.g. `--continue`). +- A tool SHOULD support named profiles/environments (service URL, credentials, defaults) selected with `--env`. +- Persisted data MUST reside under a conventional configuration path, MUST NOT store secrets in world-readable files (secret files MUST be mode `0600` or the platform equivalent), and MUST be inspectable and clearable by the user. +- Explicit options MUST override stored state. + +--- + +## 7. Task status & polling + +### 7.1 Task states + +Task states: `SUBMITTED`, `WORKING`, `INPUT_REQUIRED`, `AUTH_REQUIRED`, `COMPLETED`, `FAILED`, `CANCELED`, `REJECTED` (A2A §4.1.3). The canonical wire values are the A2A `TaskState` protobuf enum names (`TASK_STATE_*`, e.g. `TASK_STATE_COMPLETED`); this document uses the short forms for readability. + +- **Terminal:** `COMPLETED`, `FAILED`, `CANCELED`, `REJECTED`. Streams close; no further messages are accepted. +- **Interrupted (caller action required):** `INPUT_REQUIRED`, `AUTH_REQUIRED`. A tool MUST stop waiting and return/prompt so the caller can act (§6.2). + +### 7.2 Update-delivery mechanisms + +A2A provides three ways to observe task progress (A2A §3.5). A conformant tool MUST implement **polling**, SHOULD implement **streaming**, and MAY implement **push notifications** (Tier 3): + +1. **Streaming (SSE)** — live status/artifact events; the first event MUST be the `Task`. Available only when the Agent Card advertises the streaming capability. +2. **Polling** — repeated `get` until a terminal or interrupted state. Always available; the REQUIRED fallback when streaming is unsupported or a connection drops. +3. **Push notifications** — server-initiated webhook callbacks (Tier 3); require the tool to host a receiver, which is beyond the client baseline. + +### 7.3 Polling behavior (MUST) + +A conformant tool MUST provide a polling path: + +- **`get `** — one-shot retrieval of task state (with artifacts via `--include-artifacts`, history via `--history `). +- **A blocking/watch mode** that repeatedly polls until a **terminal** state and **stops immediately on an interrupted** state, returning so the caller can act. This is the default behavior of `send` (§4.5) and is available on `get` via `--wait` / `--watch`. +- Polling controls: `--poll-interval` (RECOMMENDED default 2 seconds) and `--timeout` (on expiry the tool MUST exit non-zero with the timeout code, §9). A tool SHOULD apply bounded backoff, MUST NOT busy-loop, and MUST remain interruptible without losing the already-printed `taskId`. +- When both streaming and polling are available, a blocking wait MAY prefer streaming and MUST fall back to polling on stream failure. Because messages are not a reliable delivery mechanism, after any stream reconnect the tool MUST reconcile final state with a `get`. + +### 7.4 Stream resumption (SHOULD) + +For long-running tasks, a tool SHOULD support reconnection via `subscribe` (whose first event is the `Task`, closing the gap between a poll and a subscribe) and, where the server supports it, resumption from the last received event. After any reconnect, the tool MUST reconcile with a `get`. + +--- + +## 8. Command specifications + +### 8.1 `discover` (Tier 1, MUST) +Fetch the Agent Card from the well-known location (`/.well-known/agent-card.json`) or an explicit `--card-url`, then parse and present: identity, advertised capabilities (streaming, push notifications, extended card), declared interfaces/transports, security schemes, and skills. The tool MUST use the card to select a transport (§11). It SHOULD offer `--validate` to check the card against the A2A schema and SHOULD cache the card honoring HTTP caching semantics. + +### 8.2 `send` (Tier 1, MUST) +Send a message to **start or continue** a conversation. +- Blocking by default (the operation waits until the task reaches a terminal or interrupted state, §4.5); `--async` / `--return-immediately` returns the `taskId` immediately instead. +- Accepts `--context-id` / `--task-id` (§6.2); message parts via `--text`, `--file`, `--data`; `--stream` (§7.2); polling controls (§7.3). +- With `--stream`, the tool consumes the event stream when the streaming capability is present; the first event MUST be the `Task`. If streaming is unsupported the tool MUST fall back or error clearly and MUST NOT hang. +- On `INPUT_REQUIRED` or `AUTH_REQUIRED`, the tool MUST stop and report the `taskId`, `contextId`, and state with a resume hint (§6.3), and MUST NOT deadlock. +- The tool MUST render produced artifacts. + +### 8.3 `get` (Tier 1, MUST) +Retrieve a task by identifier: state, artifacts (`--include-artifacts`), and optionally history (`--history `). One-shot by default; `--wait` / `--watch` polls until a terminal or interrupted state (§7.3). MUST report `taskId`, `contextId`, and state. + +### 8.4 `cancel` (Tier 1, MUST) +Cancel an active task by identifier. The operation is idempotent and MAY return a not-cancelable error if the task has already reached a terminal state. MUST report the resulting state. + +### 8.5 Higher-tier commands (outline) +- **Tier 2:** `list` (cursor-paginated, filterable by status and context); `subscribe` (stream reconnect); `auth login` (OAuth 2.1 device-code and client-credentials flows with a secure token store); multi-transport selection; `config` (named profiles); `chat` (interactive multi-turn); `download` (save artifacts); `--dump-wire`; `conformance` (TCK smoke check); shell completions. +- **Tier 3:** `push-config` create/get/list/delete plus a webhook receiver; gRPC transport; authenticated extended Agent Card; Agent Card signature verification; mTLS; OpenID Connect; `serve`/mock agent; catalog/registry integration; batch/stdin input; protocol extensions. + +--- + +## 9. Output & exit codes + +9.1 In `--output json`, a tool MUST emit only valid JSON or NDJSON on **stdout**; all diagnostics, prompts, and logs MUST go to **stderr**. The two streams MUST NOT be mixed. + +9.2 When `tui` is in effect (selected explicitly or by configuration), a tool MUST auto-degrade to `text` if stdout is not a terminal, producing no terminal control sequences and never blocking on interactive input. The default output mode is already non-interactive `text` (§4.5). + +9.3 JSON output for task operations MUST conform to the minimal envelope in Appendix B, including the stable fields defined in §6.3. + +9.4 Errors MUST be machine-readable in JSON mode (the error envelope in Appendix B) and MUST be normalized across transports so that the same A2A error yields the same tool-level result regardless of binding. + +9.5 Exit codes: + +| Code | Meaning | +| --- | --- | +| 0 | Success / task completed | +| 1 | Generic failure | +| 2 | Usage error | +| 3 | Agent or transport unreachable | +| 4 | Authentication required or failed | +| 5 | Task failed or rejected | +| 6 | Input required (non-interactive) | +| 7 | Timeout | + +--- + +## 10. Authentication + +10.1 **Tier 1 (MUST):** scriptable, caller-supplied credentials — `--bearer`, `--api-key`, and `-H/--header`, with environment-variable equivalents. Credentials are attached per request in transport headers or metadata (A2A conveys identity at the transport layer, not in the payload). + +10.2 **Tier 2 (SHOULD):** interactive OAuth 2.1 via `auth login`, supporting the device-code flow (designed for CLIs) and the client-credentials flow, with secure token storage and automatic attachment on subsequent calls. + +10.3 **Tier 3 (MAY):** mutual TLS and OpenID Connect. A tool at this tier SHOULD also handle the in-task `AUTH_REQUIRED` state, a second authentication path that can occur mid-task. + +10.4 Fetching the authenticated extended Agent Card MUST use a security scheme advertised on the public Agent Card. + +--- + +## 11. Transport & version negotiation + +11.1 **Transport selection (MUST):** a tool MUST select a binding from the Agent Card's declared interfaces (honoring the declared preference order) and MUST NOT assume a single transport. When the card offers multiple bindings without a decisive preference, the tool defaults to **HTTP+JSON** (§4.5), overridable with `--transport`. When an interface declares a routing identifier, the tool MUST echo it on every request. + +11.2 **Protocol version (MUST):** a tool MUST signal the A2A protocol version on every request. This is a per-binding service parameter conveyed as an HTTP header, a query parameter, or gRPC metadata depending on the transport; an empty value causes the server to assume a legacy version, so the tool MUST set it explicitly. The tool SHOULD expose `--a2a-version` and MUST surface a version-unsupported error clearly rather than silently downgrading. + +11.3 **Capability validation (SHOULD):** before invoking a capability-gated operation (streaming, push notifications, extended card), a tool SHOULD verify the capability on the Agent Card, and MUST declare any server-required extensions it supports. + +--- + +## 12. Agent integration (SKILL.md) + +12.1 A conformant tool MUST ship a machine-readable agent skill descriptor (`SKILL.md`) that instructs an AI coding agent how to drive the tool: prefer `--output json`; rely on blocking completion (the default, or `--wait` on `get`) rather than ad-hoc sleeps; determine success from the reported task state; **capture and replay `taskId` and `contextId` to sustain a multi-turn conversation**; and use scriptable credentials rather than interactive login. + +12.2 **One lightweight, generic skill.** A conformant tool MUST ship exactly one skill descriptor — not one per tier, per command, or per capability. It MUST be generic and token-efficient: it MUST NOT enumerate the full command surface or embed all capabilities inline, and instead MUST direct the agent to discover capabilities at runtime (for example, beginning with `a2a-cli help` / `a2a-cli --help`), keeping the always-loaded context footprint minimal. + +12.3 **Distinct layers; bundled and self-installable.** The specification (the behavioral contract) and the skill (agent-facing usage guidance) are DISTINCT layers and MUST be maintained separately; the skill MUST NOT restate normative requirements. The tool and its skill SHOULD be distributed as a single bundle that an AI coding agent can discover and install itself, following an established agent-plugin convention. + +--- + +## 13. Compliance report & compatibility matrix + +13.1 A tool asserting conformance MUST publish a **compliance report** stating: the tier claimed; per-command pass/fail; the A2A TCK version(s) exercised; transports covered; and conformance to the conversation/session (§6) and polling (§7) requirements. + +13.2 The A2A project publishes a **compatibility matrix** (tools × features/tiers) so users can compare implementations. Entries are backed by compliance reports rather than being self-asserted alone. This evidence-based matrix — not self-declaration — is what substantiates a tool's advertised tier. + +--- + +## 14. Non-goals + +This specification does not: define server/agent behavior (`serve` mode is optional and out of the client baseline); alter A2A wire semantics; or mandate an implementation language or framework for *conformant* tools (the official reference implementation is a separate, project-level designation — §15). + +--- + +## 15. Governance & official status + +15.1 **Ownership & ratification.** This specification is maintained under the A2A project's public GitHub organization and is ratified through the A2A project's governance process (its Technical Steering Committee). Status advances along the document axis **Draft → Proposed → Ratified**; only a Ratified version is "official" as a specification. + +15.2 **Two independent status axes.** Specification status (§15.1) is independent of *implementation* maturity (**alpha → beta → GA**). An official reference implementation MAY be released as an early alpha, with no stability guarantees, while the specification is still Proposed. + +15.3 **Official reference implementation.** The A2A project designates a **single official reference implementation**, written in Go and maintained under the A2A project's GitHub organization. Additional conformant community implementations, in any language, are welcomed and listed in the compatibility matrix (§13.2). Designating one official implementation does not restrict conformance (§1.4); it concentrates the project's maintenance and the canonical `SKILL.md` in one place. + +15.4 **Change control.** Changes to normative requirements MUST go through the ratification process and MUST bump the specification version. The revision history (Appendix D) records each change. + +--- + +## Appendix A — Command to A2A operation mapping (informative) + +| Command | A2A operation | A2A reference | Tier | +| --- | --- | --- | --- | +| `discover` | Get Agent Card / Get Extended Agent Card | §8 / §3.1.11 | 1 (extended: 3) | +| `send` | Send Message / Send Streaming Message | §3.1.1 / §3.1.2 | 1 | +| `get` | Get Task | §3.1.3 | 1 | +| `cancel` | Cancel Task | §3.1.5 | 1 | +| `list` | List Tasks | §3.1.4 | 2 | +| `subscribe` | Subscribe to Task | §3.1.6 | 2 | +| `push-config` | Create / Get / List / Delete Push Notification Config | §3.1.7–§3.1.10 | 3 | + +## Appendix B — Minimal JSON output envelope (normative) + +In `--output json`, task-affecting commands (`send`, `get`, `cancel`, and per-item `list`/`subscribe` output) MUST emit at least the following fields. Tools MAY add fields; consumers MUST ignore unknown fields. + +**Task-operation object:** +```json +{ + "taskId": "string | null", + "contextId": "string | null", + "state": "TASK_STATE_*", + "artifacts": [], + "message": null +} +``` +- `taskId`, `contextId`, `state` — REQUIRED (may be `null` only when no task was created, e.g. a direct message response). +- `artifacts` — REQUIRED when artifacts were requested/available; otherwise MAY be omitted or empty. +- `message` — the direct message response when the server returned a Message rather than a Task; otherwise `null`. + +**Error object** (mutually exclusive with a successful result): +```json +{ + "error": { + "code": "string", + "message": "string", + "a2aCode": "string | number | null" + } +} +``` +- `code` — REQUIRED, the normalized CLI error identifier (stable across transports). +- `message` — REQUIRED, human-readable. +- `a2aCode` — the underlying A2A/transport error code when one exists, else `null`. + +**Streaming (`-n` / NDJSON):** one JSON object per line; each object MUST carry a `type` field identifying the event, and terminal events MUST include the task-operation fields above. + +## Appendix C — References + +- A2A Protocol Specification v1.0 — https://a2a-protocol.org/latest/specification/ +- A2A Agent Discovery — https://a2a-protocol.org/latest/topics/agent-discovery/ +- A2A Streaming & Asynchronous Operations — https://a2a-protocol.org/latest/topics/streaming-and-async/ +- RFC 2119 — Key words for requirement levels +- RFC 8628 — OAuth 2.0 Device Authorization Grant + +## Appendix D — Revision history + +| Version | Date | Notes | +| --- | --- | --- | +| 0.1 | 2026-08 | Initial published draft. Tier 1 normative; Tiers 2–3 outlined. Client-only baseline; conversation/session state (§6) and task polling (§7) as first-class; opinionated defaults (§4.5); conformant-vs-official + governance (§1.4, §15); normative JSON envelope (Appendix B). | From b15a199e14cf71bf60b3b3e8d1479a7e37d96004 Mon Sep 17 00:00:00 2001 From: Sampath Kumar Date: Tue, 4 Aug 2026 17:56:44 +0000 Subject: [PATCH 02/31] Keep the specification implementation-neutral MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The governance section named a single official reference implementation and its language. That is a project decision, not a behavioral requirement, and it contradicted the specification's own language-agnostic stance (§4.4) and non-goals (§14). It also risked reading as though implementations in other languages were second-class. §15.3 now states only that the project MAY designate reference implementations, that the designation is recorded outside this document, and that it neither restricts nor privileges conformance. Aligned the pointers in §1.4, §14, §15.2 and the directory README. Which implementations are designated official belongs in project governance and the compatibility matrix, not in the specification. --- specification/README.md | 2 +- specification/SPEC.md | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/specification/README.md b/specification/README.md index d114eec..6c983ff 100644 --- a/specification/README.md +++ b/specification/README.md @@ -30,7 +30,7 @@ See [`SPEC.md`](./SPEC.md) for the normative detail. 3. Copy [`COMPLIANCE.md`](./COMPLIANCE.md) into your tool's repository and fill it in. 4. Submit it to be listed in the A2A **compatibility matrix**. -Conformance is open to any implementation, in any language. "Official" is a separate, project-level designation (see `SPEC.md` §1.4 and §15); designating an official reference implementation does not restrict conformance. +Conformance is open to any implementation, in any language. "Official" is a separate, project-level designation (see `SPEC.md` §1.4 and §15.3) made outside the specification; it does not restrict or privilege conformance. ## Status diff --git a/specification/SPEC.md b/specification/SPEC.md index 15877ca..bb5a911 100644 --- a/specification/SPEC.md +++ b/specification/SPEC.md @@ -30,7 +30,7 @@ References of the form "A2A §x" point to the A2A Protocol Specification v1.0. W 1.4 **Conformant vs. official.** These are distinct: - A **conformant** tool is any implementation that satisfies a tier of this specification and publishes a compliance report (§13). Conformance is open to all. -- An **official** tool is one the A2A project has designated as a project-maintained reference implementation, hosted under the A2A project's GitHub organization, demonstrated against the A2A TCK, and listed in the compatibility matrix (§13). "Official" denotes governance and demonstrated conformance — not exclusivity. Governance and the current official implementation(s) are defined in §15. +- An **official** tool is one the A2A project has designated as a project-maintained reference implementation, hosted under the A2A project's GitHub organization, demonstrated against the A2A TCK, and listed in the compatibility matrix (§13). "Official" denotes governance and demonstrated conformance — not exclusivity, and not a higher standard of conformance. Designation is a project decision made outside this specification (§15.3). --- @@ -293,7 +293,7 @@ Cancel an active task by identifier. The operation is idempotent and MAY return ## 14. Non-goals -This specification does not: define server/agent behavior (`serve` mode is optional and out of the client baseline); alter A2A wire semantics; or mandate an implementation language or framework for *conformant* tools (the official reference implementation is a separate, project-level designation — §15). +This specification does not: define server/agent behavior (`serve` mode is optional and out of the client baseline); alter A2A wire semantics; mandate an implementation language or framework; or designate which implementations are official — that is a project decision made outside this document (§15.3). --- @@ -301,9 +301,9 @@ This specification does not: define server/agent behavior (`serve` mode is optio 15.1 **Ownership & ratification.** This specification is maintained under the A2A project's public GitHub organization and is ratified through the A2A project's governance process (its Technical Steering Committee). Status advances along the document axis **Draft → Proposed → Ratified**; only a Ratified version is "official" as a specification. -15.2 **Two independent status axes.** Specification status (§15.1) is independent of *implementation* maturity (**alpha → beta → GA**). An official reference implementation MAY be released as an early alpha, with no stability guarantees, while the specification is still Proposed. +15.2 **Two independent status axes.** Specification status (§15.1) is independent of *implementation* maturity (**alpha → beta → GA**). An implementation MAY be released as an early alpha, with no stability guarantees, while the specification is still a Draft or Proposed. -15.3 **Official reference implementation.** The A2A project designates a **single official reference implementation**, written in Go and maintained under the A2A project's GitHub organization. Additional conformant community implementations, in any language, are welcomed and listed in the compatibility matrix (§13.2). Designating one official implementation does not restrict conformance (§1.4); it concentrates the project's maintenance and the canonical `SKILL.md` in one place. +15.3 **Reference implementations.** The A2A project MAY designate one or more reference implementations. Such a designation is a project decision recorded outside this specification; which implementations hold it, and in which languages, is published alongside the compatibility matrix (§13.2). This specification is language- and implementation-neutral: designation confers no additional normative authority, and it neither restricts nor privileges conformance (§1.4), which remains open to any implementation in any language. 15.4 **Change control.** Changes to normative requirements MUST go through the ratification process and MUST bump the specification version. The revision history (Appendix D) records each change. From a78fee21c6bc5565a117193e14f510683f4e8676 Mon Sep 17 00:00:00 2001 From: Sampath Kumar Date: Thu, 6 Aug 2026 14:54:05 +0000 Subject: [PATCH 03/31] Support both JSON and JSONL output; rename NDJSON to JSONL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The draft conflated the two: `-n` was described as an alias for `--output json` "(NDJSON)", leaving it ambiguous whether machine-readable output was one document or a stream of them. They are different contracts for different consumers, so the specification now defines both. - `json` emits exactly one complete document, buffered even when the underlying interaction streams, so a caller can parse stdout in one shot. This suits fast agents and callers that want the whole result at once. - `jsonl` emits one complete JSON object per line, flushed as produced, so agentic apps and harnesses that consume streaming output can act on partial results. A single-line result is valid JSONL. Both modes share the Appendix B envelope and the §6.3 identifier fields. Also: renamed NDJSON to JSONL throughout, added `--no-wait` as an alias for `--async` / `--return-immediately`, and made explicit that not waiting still emits `taskId` and `contextId` so the caller can poll later. Renumbered the exit-code subsection to §9.6 and expanded the compliance checklist to cover each mode separately. --- specification/COMPLIANCE.md | 21 ++++++++++-------- specification/SPEC.md | 44 +++++++++++++++++++++++++------------ 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/specification/COMPLIANCE.md b/specification/COMPLIANCE.md index 82f07f1..357d4a2 100644 --- a/specification/COMPLIANCE.md +++ b/specification/COMPLIANCE.md @@ -37,15 +37,18 @@ | 8 | Session persistence *(if state is persisted)* — conventional path, secrets not world-readable (`0600`), explicit flags override stored state | §6.4 | `<>` | | | 9 | Polling path — `get` + wait/watch with `--poll-interval` / `--timeout` | §7.3 | `<>` | | | 10 | Handles interrupted states (`INPUT_REQUIRED` / `AUTH_REQUIRED`) without deadlock | §7.1, §8.2 | `<>` | | -| 11 | Output contract — `--output json` on stdout, diagnostics on stderr, auto-degrade off-TTY | §9.1, §9.2 | `<>` | | -| 12 | Minimal JSON envelope (`taskId`/`contextId`/`state`, error object) | §9.3, Appendix B | `<>` | | -| 13 | Errors machine-readable **and normalized across transports** (same A2A error → same result) | §9.4 | `<>` | | -| 14 | Exit-code scheme (0–7) | §9.5 | `<>` | | -| 15 | Tier-1 auth — bearer / API key / custom header (scriptable) | §10.1 | `<>` | | -| 16 | Transport selection from the Agent Card; HTTP+JSON default | §11.1, §4.5 | `<>` | | -| 17 | `A2A-Version` signaled on every request (explicit; no silent downgrade) | §11.2 | `<>` | | -| 18 | Opinionated defaults, each overridable by flag | §4.5 | `<>` | | -| 19 | Ships exactly one lightweight, generic `SKILL.md`; spec & skill kept as distinct layers (skill does not restate normative requirements) | §12.1–§12.3 | `<>` | | +| 11 | Output contract — structured payload on stdout, diagnostics on stderr, auto-degrade off-TTY | §9.1, §9.2 | `<>` | | +| 12 | `--output json` — exactly one complete document, buffered even when the interaction streams | §9.3 | `<>` | | +| 13 | `--output jsonl` — one complete JSON object per line, flushed as produced | §9.3 | `<>` | | +| 14 | Minimal envelope (`taskId`/`contextId`/`state`, error object) in both machine-readable modes | Appendix B | `<>` | | +| 15 | Errors machine-readable **and normalized across transports** (same A2A error → same result) | §9.4 | `<>` | | +| 16 | Async (`--no-wait`) still emits `taskId` + `contextId` for later polling | §9.5 | `<>` | | +| 17 | Exit-code scheme (0–7) | §9.6 | `<>` | | +| 18 | Tier-1 auth — bearer / API key / custom header (scriptable) | §10.1 | `<>` | | +| 19 | Transport selection from the Agent Card; HTTP+JSON default | §11.1, §4.5 | `<>` | | +| 20 | `A2A-Version` signaled on every request (explicit; no silent downgrade) | §11.2 | `<>` | | +| 21 | Opinionated defaults, each overridable by flag | §4.5 | `<>` | | +| 22 | Ships exactly one lightweight, generic `SKILL.md`; spec & skill kept as distinct layers (skill does not restate normative requirements) | §12.1–§12.3 | `<>` | | ## 4. Tier 2 — Standard diff --git a/specification/SPEC.md b/specification/SPEC.md index bb5a911..3a8725b 100644 --- a/specification/SPEC.md +++ b/specification/SPEC.md @@ -64,7 +64,7 @@ Restated from A2A §4 so this document stands alone: 4.1 **Agent-first core, dual-mode.** The default behavioral contract MUST be safe for non-interactive and programmatic use (structured output, no interactive prompts required, deterministic exit codes). A rich interactive experience for humans MAY be layered on top and MUST be gated by terminal (TTY) detection. A conformant tool is a single dual-mode tool, not two separate tools. -4.2 **Stable, versioned contract.** The `--output json` shape (§9, Appendix B) and the exit-code scheme (§9) are a stable contract; breaking changes require a specification version bump. +4.2 **Stable, versioned contract.** The machine-readable output shapes (`json` and `jsonl` — §9.3, Appendix B) and the exit-code scheme (§9.6) are a stable contract; breaking changes require a specification version bump. 4.3 **Explicit, recoverable state.** Every identifier needed to resume a conversation MUST be observable in output (§6.3). Conversation state MUST NOT exist only in process memory. @@ -75,8 +75,8 @@ Restated from A2A §4 so this document stands alone: | Behavior | Default | Override | | --- | --- | --- | | Transport | **HTTP+JSON**, when the Agent Card offers a choice or expresses no preference (subject to card-driven selection, §11.1) | `--transport ` | -| Task completion | **Wait** (block) until the task reaches a terminal or interrupted state | `--async` / `--return-immediately` (return the `taskId` immediately) | -| Output presentation | **Human-readable, minimal yet consistently structured** text (labeled fields; not raw JSON, not a verbose dashboard) | `--output `, `-n` (JSON/NDJSON) | +| Task completion | **Wait** (block) until the task reaches a terminal or interrupted state | `--async` / `--return-immediately` / `--no-wait` (return identifiers immediately) | +| Output presentation | **Human-readable, minimal yet consistently structured** text (labeled fields; not raw JSON, not a verbose dashboard) | `--output `, `-n` (machine-readable `json`) | | Detail level | **Concise** | `-v, --verbose` for detailed output | | Protocol version | The **latest** A2A version the tool supports, signaled explicitly (§11.2) | `--a2a-version ` | | Transport security | **TLS verification enabled** | `--insecure` (development only; MUST warn) | @@ -111,10 +111,10 @@ Restated from A2A §4 so this document stands alone: | `--context-id ` | Continue an existing conversation (§6.2). | | `--task-id ` | Continue an existing task (§6.2). | | `--continue` / `--last` | Resume the stored last conversation (§6.4). | -| `-o, --output ` | Output mode. Default: minimal, structured `text` (§4.5). `tui` is an opt-in interactive mode; `json` is machine-readable. | -| `-n` | Alias for `--output json` (NDJSON), non-interactive. | +| `-o, --output ` | Output mode. Default: minimal, structured `text` (§4.5). `tui` is an opt-in interactive mode; `json` emits one complete document; `jsonl` emits one JSON object per line for streaming (§9.3). | +| `-n` | Alias for `--output json`, non-interactive. | | `--transport ` | Override the transport binding (default HTTP+JSON, §4.5), subject to the Agent Card (§11.1). | -| `--async` / `--return-immediately` | Do not wait; return the `taskId` immediately (default is to wait, §4.5 / §7.3). | +| `--async` / `--return-immediately` / `--no-wait` | Do not wait; return the task identifiers immediately for later polling (default is to wait, §4.5 / §7.3). | | `--wait` / `--watch` | Block until the task reaches a terminal or interrupted state. This is the default for `send` (§4.5); stating it explicitly overrides a configured default. On `get` it turns the one-shot read into a poll loop (§7.3). | | `--poll-interval ` / `--timeout ` | Polling controls (§7.3). | | `--bearer ` / `--api-key ` / `-H, --header ` | Credentials (§10.1). | @@ -228,15 +228,29 @@ Cancel an active task by identifier. The operation is idempotent and MAY return ## 9. Output & exit codes -9.1 In `--output json`, a tool MUST emit only valid JSON or NDJSON on **stdout**; all diagnostics, prompts, and logs MUST go to **stderr**. The two streams MUST NOT be mixed. +9.1 In a machine-readable mode (`json` or `jsonl`), a tool MUST emit only the structured payload on **stdout**; all diagnostics, prompts, progress indicators, and logs MUST go to **stderr**. The two streams MUST NOT be mixed. 9.2 When `tui` is in effect (selected explicitly or by configuration), a tool MUST auto-degrade to `text` if stdout is not a terminal, producing no terminal control sequences and never blocking on interactive input. The default output mode is already non-interactive `text` (§4.5). -9.3 JSON output for task operations MUST conform to the minimal envelope in Appendix B, including the stable fields defined in §6.3. +### 9.3 Machine-readable modes: `json` and `jsonl` -9.4 Errors MUST be machine-readable in JSON mode (the error envelope in Appendix B) and MUST be normalized across transports so that the same A2A error yields the same tool-level result regardless of binding. +A conformant tool MUST support **both** machine-readable modes. They serve different consumers and MUST NOT be conflated. -9.5 Exit codes: +| Mode | Shape | Use it when | +| --- | --- | --- | +| **`json`** | Exactly **one** complete JSON document written once, when the result is known | The agent responds quickly, or the caller wants the whole result in a single parse — the common scripting case | +| **`jsonl`** | **One JSON object per line** ([JSON Lines](https://jsonlines.org/)), flushed as each event occurs | The caller consumes progress incrementally — streaming agents, and agentic apps/harnesses that render or act on partial output | + +- **`json` MUST buffer**: even when the underlying interaction streams, the tool MUST emit a single final document, never a concatenation of objects. A `json` consumer can always `JSON.parse` stdout in one shot. +- **`jsonl` MUST stream**: each line MUST be a complete, independently parseable JSON object terminated by a newline, flushed as it is produced so a reader can consume the stream incrementally. Lines MUST NOT be pretty-printed across multiple physical lines. +- If a tool cannot stream a given interaction (streaming unsupported by the agent, or a one-shot command such as `cancel`), `jsonl` MUST still be honored by emitting the applicable object(s), one per line — a single-line result is valid JSONL. +- Both modes MUST use the envelope in Appendix B and MUST include the stable fields defined in §6.3. + +9.4 Errors MUST be machine-readable in both modes (the error envelope in Appendix B) and MUST be normalized across transports so that the same A2A error yields the same tool-level result regardless of binding. In `jsonl`, an error terminating the stream MUST be emitted as a final error object on its own line. + +9.5 When the caller does not wait for completion (`--async` / `--return-immediately` / `--no-wait`), the tool MUST still emit a result object carrying the identifiers required to resume or poll later — at minimum `taskId` and `contextId` (§6.3) — so the caller can query status with `get` at a later time. + +9.6 Exit codes: | Code | Meaning | | --- | --- | @@ -275,7 +289,7 @@ Cancel an active task by identifier. The operation is idempotent and MAY return ## 12. Agent integration (SKILL.md) -12.1 A conformant tool MUST ship a machine-readable agent skill descriptor (`SKILL.md`) that instructs an AI coding agent how to drive the tool: prefer `--output json`; rely on blocking completion (the default, or `--wait` on `get`) rather than ad-hoc sleeps; determine success from the reported task state; **capture and replay `taskId` and `contextId` to sustain a multi-turn conversation**; and use scriptable credentials rather than interactive login. +12.1 A conformant tool MUST ship a machine-readable agent skill descriptor (`SKILL.md`) that instructs an AI coding agent how to drive the tool: use `--output json` for a single parseable result, or `--output jsonl` to consume progress incrementally (§9.3); rely on blocking completion (the default, or `--wait` on `get`) rather than ad-hoc sleeps; determine success from the reported task state; **capture and replay `taskId` and `contextId` to sustain a multi-turn conversation**; and use scriptable credentials rather than interactive login. 12.2 **One lightweight, generic skill.** A conformant tool MUST ship exactly one skill descriptor — not one per tier, per command, or per capability. It MUST be generic and token-efficient: it MUST NOT enumerate the full command surface or embed all capabilities inline, and instead MUST direct the agent to discover capabilities at runtime (for example, beginning with `a2a-cli help` / `a2a-cli --help`), keeping the always-loaded context footprint minimal. @@ -323,7 +337,7 @@ This specification does not: define server/agent behavior (`serve` mode is optio ## Appendix B — Minimal JSON output envelope (normative) -In `--output json`, task-affecting commands (`send`, `get`, `cancel`, and per-item `list`/`subscribe` output) MUST emit at least the following fields. Tools MAY add fields; consumers MUST ignore unknown fields. +In a machine-readable mode (`json` or `jsonl`), task-affecting commands (`send`, `get`, `cancel`, and per-item `list`/`subscribe` output) MUST emit at least the following fields. Tools MAY add fields; consumers MUST ignore unknown fields. **Task-operation object:** ```json @@ -353,7 +367,9 @@ In `--output json`, task-affecting commands (`send`, `get`, `cancel`, and per-it - `message` — REQUIRED, human-readable. - `a2aCode` — the underlying A2A/transport error code when one exists, else `null`. -**Streaming (`-n` / NDJSON):** one JSON object per line; each object MUST carry a `type` field identifying the event, and terminal events MUST include the task-operation fields above. +**Streaming (`--output jsonl`):** one JSON object per line, flushed as produced. Each line MUST carry a `type` field identifying the event (for example `status`, `artifact`, `result`, `error`), and the final/terminal line MUST include the task-operation fields above so a reader that keeps only the last line still obtains `taskId`, `contextId`, and `state`. + +**Single document (`--output json`):** exactly one task-operation object (or one error object) for the whole invocation. ## Appendix C — References @@ -367,4 +383,4 @@ In `--output json`, task-affecting commands (`send`, `get`, `cancel`, and per-it | Version | Date | Notes | | --- | --- | --- | -| 0.1 | 2026-08 | Initial published draft. Tier 1 normative; Tiers 2–3 outlined. Client-only baseline; conversation/session state (§6) and task polling (§7) as first-class; opinionated defaults (§4.5); conformant-vs-official + governance (§1.4, §15); normative JSON envelope (Appendix B). | +| 0.1 | 2026-08 | Initial published draft. Tier 1 normative; Tiers 2–3 outlined. Client-only baseline; conversation/session state (§6) and task polling (§7) as first-class; opinionated defaults (§4.5); conformant-vs-official + governance (§1.4, §15); two machine-readable output modes, `json` and `jsonl` (§9.3); normative output envelope (Appendix B). | From a467f342dbff9e052db49d9b3859fdba45e8a923 Mon Sep 17 00:00:00 2001 From: Sampath Kumar Date: Thu, 6 Aug 2026 15:13:34 +0000 Subject: [PATCH 04/31] Add a last-updated date and scope change control to Draft status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The header carried a version and status but no date, so a reader could not tell how current the document was. It now records when it last changed. §15.4 also required a version bump for any normative change, which the document was already breaking: it has stayed at 0.1 across two normative revisions. That rule belongs to a ratified specification, not to a draft still being assembled. It now applies from the first Proposed version, and while in Draft the revision history and the last-updated date carry the record instead. Implementers are told to pin to a ratified version for conformance claims. Appendix D now lists dated draft revisions rather than a single summary row. --- specification/SPEC.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/specification/SPEC.md b/specification/SPEC.md index 3a8725b..cf2904d 100644 --- a/specification/SPEC.md +++ b/specification/SPEC.md @@ -2,6 +2,7 @@ **Version:** 0.1 **Status:** Draft — open for review. +**Last updated:** 2026-08-06 **Applies to:** A2A Protocol v1.0 ## Abstract @@ -319,7 +320,7 @@ This specification does not: define server/agent behavior (`serve` mode is optio 15.3 **Reference implementations.** The A2A project MAY designate one or more reference implementations. Such a designation is a project decision recorded outside this specification; which implementations hold it, and in which languages, is published alongside the compatibility matrix (§13.2). This specification is language- and implementation-neutral: designation confers no additional normative authority, and it neither restricts nor privileges conformance (§1.4), which remains open to any implementation in any language. -15.4 **Change control.** Changes to normative requirements MUST go through the ratification process and MUST bump the specification version. The revision history (Appendix D) records each change. +15.4 **Change control.** While the specification is a **Draft**, it is still being assembled: normative requirements MAY change without a version bump, and each notable revision is recorded by date in the revision history (Appendix D) and reflected in the **Last updated** date in the header. From the first **Proposed** version onward, any change to a normative requirement MUST go through the ratification process and MUST bump the specification version. Implementers SHOULD therefore treat a Draft as a moving target and pin to a ratified version for conformance claims. --- @@ -381,6 +382,10 @@ In a machine-readable mode (`json` or `jsonl`), task-affecting commands (`send`, ## Appendix D — Revision history +While the specification is in Draft, notable revisions are recorded by date; the version number changes only at ratification milestones (§15.4). Newest first. + | Version | Date | Notes | | --- | --- | --- | -| 0.1 | 2026-08 | Initial published draft. Tier 1 normative; Tiers 2–3 outlined. Client-only baseline; conversation/session state (§6) and task polling (§7) as first-class; opinionated defaults (§4.5); conformant-vs-official + governance (§1.4, §15); two machine-readable output modes, `json` and `jsonl` (§9.3); normative output envelope (Appendix B). | +| 0.1 (Draft) | 2026-08-06 | Defined two machine-readable output modes — `json` (exactly one buffered document) and `jsonl` (one object per line, flushed as produced) — and required both (§9.3). Renamed NDJSON to JSONL throughout. Added `--no-wait` as an alias for `--async` / `--return-immediately`, and required that not waiting still returns `taskId` and `contextId` for later polling (§9.5). | +| 0.1 (Draft) | 2026-08-04 | Made governance implementation-neutral: removed the designation of a specific reference implementation and language from §15.3, and aligned §1.4, §14 and §15.2. | +| 0.1 (Draft) | 2026-08-04 | Initial draft. Tier 1 normative; Tiers 2–3 outlined. Client-only baseline; conversation/session state (§6) and task polling (§7) as first-class; opinionated defaults (§4.5); conformant-vs-official + governance (§1.4, §15); normative output envelope (Appendix B). | From 99c415f35f4a84a44dcd48ec751dc8123c78cb45 Mon Sep 17 00:00:00 2001 From: Sampath Kumar Date: Mon, 10 Aug 2026 11:26:11 +0000 Subject: [PATCH 05/31] Sharpen the motivation and right-size the error registry Motivation. "Why this matters" now states three distinct problems rather than four overlapping ones. The skill-file point and the coding-agent point were saying the same thing and are merged. A third motivation is added that was missing: A2A has SDKs, a TCK, an inspector and samples, but no quick way to exercise a *running* agent from outside a single SDK. A scriptable client with machine-readable output doubles as a lightweight test harness, by hand or in CI. Error registry. Nineteen required error codes was too much to ask of an implementer and risked discouraging adoption. The registry is now split: eight core codes are required and between them cover every exit code, so a caller can always act on a result; the remaining eleven become optional refinements that a tool uses only when it can genuinely tell those cases apart, falling back to the core code otherwise. The vocabulary is unchanged, the obligation is much smaller. The eight exit codes stay as they are. Each maps to a distinct, separately actionable failure, which is what the Command Line Interface Guidelines ask for: map the non-zero exit codes to the most important failure modes. Compliance report. Output checks collapse from nine to five, with OUT_001 now the single "every response says which conversation, which task, and what state" check. Skill checks collapse from three to two. Adds a short explanation of what an exit code is and why it is worth verifying, a note that the requirement list is expected to grow on request, and a results summary table. --- specification/COMPLIANCE.md | 159 +++++++++++++++++++++++------------- specification/SPEC.md | 122 +++++++++++++++++++++++++-- 2 files changed, 220 insertions(+), 61 deletions(-) diff --git a/specification/COMPLIANCE.md b/specification/COMPLIANCE.md index 357d4a2..6cb0bc4 100644 --- a/specification/COMPLIANCE.md +++ b/specification/COMPLIANCE.md @@ -2,6 +2,16 @@ > **Template.** Copy this file into your tool's repository, complete every field, and replace all `<…>` placeholders. Link the finished report from the A2A compatibility matrix. A tool MUST NOT advertise a tier it has not demonstrated here. +This file is also the **authoritative registry of requirement identifiers** (`SPEC.md` §3.3). Identifiers are permanent: once published, an identifier is never renumbered, reused, or reassigned. Tier membership is *not* encoded in the identifier, so a requirement can move tiers between specification versions and still be tracked by the same ID. Withdrawn requirements stay listed, marked `Withdrawn`, and their numbers are never reused. + +**This list is not fixed — it is expected to grow.** If you are building a tool and hit a real use case that no requirement covers, please open a request against the specification repository. It can be added in a future revision rather than left undocumented. Adding requirements never changes existing identifiers, so reports and test suites that cite them keep working. + +**Why report at this level of detail.** The project's goal is a single official CLI. A per-requirement report is what makes progress toward that goal visible: it shows where a tool stands today, what it does not do yet, and — for open-source contributors — exactly which gaps are open to pick up. + +**On authentication.** Authentication, security, and compliance are large topics that need more careful treatment than a checklist row can give them. The `A2ACLI_AUTH_*` requirements below cover the ground the specification defines today, and they are expected to expand. Treat the current coverage as a starting point, not a complete security review. + +**On exit codes.** An exit code is the number a command hands back to the shell when it finishes: `0` means success, anything else signals a failure. It matters because it is the only result a script or CI job gets without parsing output — `a2a-cli send … && deploy.sh` behaves correctly only if the tool exits non-zero when the task actually failed. The check confirms the tool returns a meaningful status rather than always `0`, and that the status agrees with the error it reported. + ## 1. Summary | Field | Value | @@ -21,71 +31,108 @@ ## 2. Legend -`✅ Pass` · `◐ Partial` · `❌ Fail` · `— N/A`. Every `◐`/`❌`/`—` MUST carry a note. +| Mark | Meaning | +| --- | --- | +| `✅ Pass` | Implemented and verified | +| `◐ Partial` | Partially implemented — state what is missing | +| `❌ Fail` | Not implemented, or does not behave as specified | +| `— N/A` | Not applicable to this tool — state why | + +Every `◐`, `❌`, or `—` MUST carry a note. + +## 3. Results at a glance + +| Tier | Requirements | `✅` | `◐` | `❌` | `—` | Tier satisfied? | +| --- | --- | --- | --- | --- | --- | --- | +| Tier 1 — Core | 29 | `<>` | `<>` | `<>` | `<>` | `` | +| Tier 2 — Standard | 13 | `<>` | `<>` | `<>` | `<>` | `` | +| Tier 3 — Advanced | 11 | `<>` | `<>` | `<>` | `<>` | `` | -## 3. Tier 1 — Core (required) +A tier is satisfied only when every requirement in it is `✅`. Tiers are cumulative (`SPEC.md` §3.1). -| # | Requirement | Spec § | Status | Notes | +## 4. Tier 1 — Core (required) + +| ID | Requirement | Spec § | Status | Notes | | --- | --- | --- | --- | --- | -| 1 | `discover` — fetch & parse Agent Card | §8.1 | `<>` | | -| 2 | `send` — start/continue; blocking by default with `--async` override | §8.2, §4.5 | `<>` | | -| 3 | `send --stream` — SSE when supported; no hang when unsupported | §8.2, §7.2 | `<>` | | -| 4 | `get` — task state, artifacts, history | §8.3 | `<>` | | -| 5 | `cancel` — idempotent cancel | §8.4 | `<>` | | -| 6 | Continuation via `--context-id` / `--task-id` (no invented IDs; no silent new task) | §6.1, §6.2 | `<>` | | -| 7 | Reports `taskId` / `contextId` / `state` back on completion & interruption | §6.3 | `<>` | | -| 8 | Session persistence *(if state is persisted)* — conventional path, secrets not world-readable (`0600`), explicit flags override stored state | §6.4 | `<>` | | -| 9 | Polling path — `get` + wait/watch with `--poll-interval` / `--timeout` | §7.3 | `<>` | | -| 10 | Handles interrupted states (`INPUT_REQUIRED` / `AUTH_REQUIRED`) without deadlock | §7.1, §8.2 | `<>` | | -| 11 | Output contract — structured payload on stdout, diagnostics on stderr, auto-degrade off-TTY | §9.1, §9.2 | `<>` | | -| 12 | `--output json` — exactly one complete document, buffered even when the interaction streams | §9.3 | `<>` | | -| 13 | `--output jsonl` — one complete JSON object per line, flushed as produced | §9.3 | `<>` | | -| 14 | Minimal envelope (`taskId`/`contextId`/`state`, error object) in both machine-readable modes | Appendix B | `<>` | | -| 15 | Errors machine-readable **and normalized across transports** (same A2A error → same result) | §9.4 | `<>` | | -| 16 | Async (`--no-wait`) still emits `taskId` + `contextId` for later polling | §9.5 | `<>` | | -| 17 | Exit-code scheme (0–7) | §9.6 | `<>` | | -| 18 | Tier-1 auth — bearer / API key / custom header (scriptable) | §10.1 | `<>` | | -| 19 | Transport selection from the Agent Card; HTTP+JSON default | §11.1, §4.5 | `<>` | | -| 20 | `A2A-Version` signaled on every request (explicit; no silent downgrade) | §11.2 | `<>` | | -| 21 | Opinionated defaults, each overridable by flag | §4.5 | `<>` | | -| 22 | Ships exactly one lightweight, generic `SKILL.md`; spec & skill kept as distinct layers (skill does not restate normative requirements) | §12.1–§12.3 | `<>` | | - -## 4. Tier 2 — Standard - -| # | Requirement | Spec § | Status | Notes | +| `A2ACLI_DISCOVER_001` | Fetch & parse an Agent Card from the well-known location or an explicit URL | §8.1 | `<>` | | +| `A2ACLI_SEND_001` | Send a message to start a conversation | §8.2 | `<>` | | +| `A2ACLI_SEND_002` | Blocking by default; `--async` / `--return-immediately` / `--no-wait` overrides | §8.2, §4.5 | `<>` | | +| `A2ACLI_SEND_003` | `--stream` consumes SSE when supported; never hangs when unsupported | §8.2, §7.2 | `<>` | | +| `A2ACLI_SEND_004` | Renders produced artifacts | §8.2 | `<>` | | +| `A2ACLI_GET_001` | Retrieve task state, artifacts, and history by identifier | §8.3 | `<>` | | +| `A2ACLI_CANCEL_001` | Cancel a task; idempotent | §8.4 | `<>` | | +| `A2ACLI_CONV_001` | Continue a conversation via `--context-id` | §6.2 | `<>` | | +| `A2ACLI_CONV_002` | Continue a task via `--task-id`; never silently starts a new task | §6.2 | `<>` | | +| `A2ACLI_CONV_003` | Never invents server-assigned identifiers | §6.1 | `<>` | | +| `A2ACLI_CONV_004` | Reports `taskId` / `contextId` / `state` on completion and interruption | §6.3 | `<>` | | +| `A2ACLI_CONV_005` | Persisted session state *(if any)*: conventional path, secrets not world-readable, explicit flags win | §6.4 | `<>` | | +| `A2ACLI_POLL_001` | Polling path available — `get` plus wait/watch | §7.3 | `<>` | | +| `A2ACLI_POLL_002` | `--poll-interval` and `--timeout` honored; bounded backoff; no busy-loop | §7.3 | `<>` | | +| `A2ACLI_POLL_003` | Stops immediately on interrupted states (`INPUT_REQUIRED` / `AUTH_REQUIRED`) without deadlock | §7.1, §7.3 | `<>` | | +| `A2ACLI_POLL_004` | Falls back to polling on stream failure and reconciles final state with `get` | §7.3 | `<>` | | +| `A2ACLI_OUT_001` | **Standard response** — every response about a task says which conversation and which task it concerns, and what state it is in (`contextId`, `taskId`, `state`), in every output mode | §6.3, §9.5, Appendix B | `<>` | | +| `A2ACLI_OUT_002` | `--output json` — exactly one complete document, buffered even when the interaction streams | §9.3 | `<>` | | +| `A2ACLI_OUT_003` | `--output jsonl` — one complete JSON object per line, flushed as produced | §9.3 | `<>` | | +| `A2ACLI_OUT_004` | Stream discipline — payload on stdout, diagnostics on stderr, never mixed; degrades cleanly when stdout is not a terminal | §9.1, §9.2 | `<>` | | +| `A2ACLI_OUT_005` | Errors are machine-readable, consistent across transports, and carry one of the eight core `A2ACLI_ERR_*` codes (extended codes optional) | §9.4, Appendix E.1 | `<>` | | +| `A2ACLI_EXIT_001` | Exit status follows the documented scheme and matches the error code reported | §9.6, Appendix E | `<>` | | +| `A2ACLI_AUTH_001` | Scriptable credentials — bearer, API key, custom header, env equivalents | §10.1 | `<>` | | +| `A2ACLI_TX_001` | Transport selected from the Agent Card, honoring declared preference | §11.1 | `<>` | | +| `A2ACLI_TX_002` | Defaults to HTTP+JSON when the card expresses no decisive preference | §11.1, §4.5 | `<>` | | +| `A2ACLI_VER_001` | Protocol version signaled explicitly on every request; no silent downgrade | §11.2 | `<>` | | +| `A2ACLI_DEFAULT_001` | Ships the baseline defaults, each overridable by an explicit flag | §4.5 | `<>` | | +| `A2ACLI_SKILL_001` | Ships exactly one skill descriptor, generic and token-efficient, deferring to runtime `help` rather than inlining the command surface | §12.1, §12.2 | `<>` | | +| `A2ACLI_SKILL_002` | Skill and specification kept as distinct layers; the skill does not restate normative requirements | §12.3 | `<>` | | + +## 5. Tier 2 — Standard + +| ID | Requirement | Spec § | Status | Notes | | --- | --- | --- | --- | --- | -| 1 | `list` — cursor-paginated, filter by status/context | §8.5 | `<>` | | -| 2 | `subscribe` — (re)subscribe / stream reconnect | §7.4, §8.5 | `<>` | | -| 3 | `auth login` — OAuth 2.1 device-code + client-credentials; secure token store | §10.2 | `<>` | | -| 4 | ≥ 2 transports selectable from the card | §11.1 | `<>` | | -| 5 | Config profiles / named environments (`--env`) | §6.4, §8.5 | `<>` | | -| 6 | Interactive `chat` — carries context/task across turns | §6.2, §8.5 | `<>` | | -| 7 | `download` — save artifacts | §8.5 | `<>` | | -| 8 | Wire debug (`--dump-wire`) | §8.5 | `<>` | | -| 9 | `conformance` — TCK smoke check | §8.5 | `<>` | | -| 10 | Shell completions | §8.5 | `<>` | | - -## 5. Tier 3 — Advanced - -| # | Requirement | Spec § | Status | Notes | +| `A2ACLI_LIST_001` | `list` — cursor-paginated, filterable by status and context | §8.5 | `<>` | | +| `A2ACLI_SUB_001` | `subscribe` — (re)subscribe to a task's event stream | §8.5 | `<>` | | +| `A2ACLI_SUB_002` | Stream resumption after disconnect, reconciled with `get` | §7.4 | `<>` | | +| `A2ACLI_AUTH_002` | `auth login` — OAuth 2.1 device-code flow | §10.2 | `<>` | | +| `A2ACLI_AUTH_003` | `auth login` — OAuth 2.1 client-credentials flow | §10.2 | `<>` | | +| `A2ACLI_AUTH_004` | Secure token storage with automatic attachment on later calls | §10.2 | `<>` | | +| `A2ACLI_TX_003` | At least two transports supported and selectable | §11.1 | `<>` | | +| `A2ACLI_CONFIG_001` | Named environments / config profiles (`--env`) | §6.4, §8.5 | `<>` | | +| `A2ACLI_CHAT_001` | Interactive `chat` carrying context and task across turns | §6.2, §8.5 | `<>` | | +| `A2ACLI_DOWNLOAD_001` | `download` — save task artifacts to disk | §8.5 | `<>` | | +| `A2ACLI_OUT_006` | Wire debug (`--dump-wire`) emits raw protocol JSON to stderr | §8.5 | `<>` | | +| `A2ACLI_CONFORM_001` | `conformance` — smoke-check a live agent against the A2A TCK | §8.5 | `<>` | | +| `A2ACLI_OUT_007` | Shell completions provided | §8.5 | `<>` | | + +## 6. Tier 3 — Advanced + +| ID | Requirement | Spec § | Status | Notes | | --- | --- | --- | --- | --- | -| 1 | `push-config` CRUD + webhook receiver | §8.5 | `<>` | | -| 2 | gRPC transport | §11.1 | `<>` | | -| 3 | Authenticated extended Agent Card | §8.5, §10.4 | `<>` | | -| 4 | Agent Card signature verification | §8.5 | `<>` | | -| 5 | mTLS | §10.3 | `<>` | | -| 6 | OpenID Connect | §10.3 | `<>` | | -| 7 | `serve` / mock agent | §8.5 | `<>` | | -| 8 | Catalog / registry integration | §8.5 | `<>` | | -| 9 | Protocol extensions | §11.3 | `<>` | | - -## 6. Test evidence +| `A2ACLI_PUSH_001` | `push-config` create / get / list / delete | §8.5 | `<>` | | +| `A2ACLI_PUSH_002` | Local webhook receiver able to accept push notifications | §8.5, §7.2 | `<>` | | +| `A2ACLI_TX_004` | gRPC transport | §11.1 | `<>` | | +| `A2ACLI_DISCOVER_002` | Authenticated extended Agent Card | §8.5, §10.4 | `<>` | | +| `A2ACLI_DISCOVER_003` | Agent Card signature verification | §8.5 | `<>` | | +| `A2ACLI_AUTH_005` | Mutual TLS | §10.3 | `<>` | | +| `A2ACLI_AUTH_006` | OpenID Connect | §10.3 | `<>` | | +| `A2ACLI_AUTH_007` | Handles in-task `AUTH_REQUIRED` resolution | §10.3 | `<>` | | +| `A2ACLI_SERVE_001` | `serve` / mock agent mode | §8.5 | `<>` | | +| `A2ACLI_DISCOVER_004` | Catalog / registry integration | §8.5 | `<>` | | +| `A2ACLI_VER_002` | Declares server-required protocol extensions | §11.3 | `<>` | | + +## 7. Test evidence - **How the tool was exercised:** `` - **Results / logs:** `` - **Environment:** `` - **Known gaps / caveats:** `` -## 7. Attestation +## 8. Error code coverage *(optional but recommended)* + +List which `A2ACLI_ERR_*` codes (Appendix E) the tool can emit. Helps consumers write reliable error handling. + +| Code | Emitted | Notes | +| --- | --- | --- | +| `` | `<>` | | + +## 9. Attestation -Reported by `` on ``. The tier claimed in §1 reflects the evidence above. +Reported by `` on ``. The tier claimed in §1 and §3 reflects the evidence above. diff --git a/specification/SPEC.md b/specification/SPEC.md index cf2904d..7bf9646 100644 --- a/specification/SPEC.md +++ b/specification/SPEC.md @@ -2,17 +2,45 @@ **Version:** 0.1 **Status:** Draft — open for review. -**Last updated:** 2026-08-06 +**Last updated:** 2026-08-10 **Applies to:** A2A Protocol v1.0 ## Abstract This document specifies the behavior that a command-line interface (CLI) tool MUST, SHOULD, and MAY exhibit to be considered a conformant **`a2a-cli`** — a terminal client for the [Agent2Agent (A2A) Protocol](https://a2a-protocol.org/latest/specification/). It exists so that independently built CLIs, in any language, converge on one predictable command surface, output contract, and conversation model — measurable through a published compliance report. -**Why this matters.** In the absence of an official command-line client, the developer experience of building, testing, and driving A2A agents through AI coding agents is prohibitively high — every team reinvents ad-hoc scripts. AI-native development needs a stable, scriptable A2A client that a coding agent can operate directly, across scenarios well beyond debugging. This specification defines that client. - This is an **implementer's specification**. Its audience is engineers building or improving an `a2a-cli`. It constrains CLI behavior only and never modifies A2A wire semantics. +## Why this matters + +Three problems, all traceable to the same cause: there is no official A2A command-line tool. + +### 1. Everyone builds their own, and they build the same thing + +At least seven independent A2A CLIs exist today, across six languages — Go, Rust, Python, TypeScript, .NET, and Swift. Two already sit inside the A2A project's own GitHub organization ([A2A#1929](https://github.com/a2aproject/A2A/issues/1929)). + +They largely re-implement the same short list of operations: send a task, continue a multi-turn conversation, check task status, read artifacts. Those are exactly the operations an official tool should cover. + +The request keeps recurring rather than resolving. An earlier CLI contribution was closed as out of scope for its repository ([PR#1323](https://github.com/a2aproject/A2A/pull/1323)); its follow-up issue was closed as a duplicate ([#1325](https://github.com/a2aproject/A2A/issues/1325)); a working command grammar was designed separately inside the Go SDK ([a2a-go#306](https://github.com/a2aproject/a2a-go/discussions/306)); and the consolidation request itself ([A2A#1929](https://github.com/a2aproject/A2A/issues/1929)) is open for community vote. + +### 2. AI coding agents have no standard way in + +Developers increasingly work through AI coding agents, and those agents drive tools from the terminal. An agent learns to use a command-line tool from a skill descriptor — so with no canonical CLI there can be no canonical skill file, and therefore no standard way for a coding agent to work with an A2A agent at all ([A2A#1929](https://github.com/a2aproject/A2A/issues/1929)). + +Agents also consume output directly. They need predictable, machine-readable results and stable exit codes rather than a screen formatted for humans, which is why machine-readable output (§9.3) and a skill descriptor (§12) are core requirements here rather than optional extras. + +### 3. Testing a running agent is harder than it should be + +A2A already provides SDKs in several languages, a Technology Compatibility Kit, an inspector, and a sample repository. What is missing is a quick way to exercise a *running* agent from outside any one SDK. + +A conformant CLI fills that gap. Because it is scriptable and emits machine-readable output, it doubles as a lightweight test harness: a shell script or a Python test can drive real conversations against a live agent, and a coding agent can do the same unattended. That lowers the cost of checking an A2A service — by hand during development, or automatically in CI — without writing client code first. + +### What this document does about it + +It defines the core client behavior — the operations above — so that every implementation can agree on one definition. Specialised needs can be built on top; the goal here is to get the common path right. + +This is not an attempt to replace the existing tools. Any tool, in any language, can implement this specification and report exactly what it supports (§13). + ## Notational conventions The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**, **SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **MAY**, and **OPTIONAL** are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119). @@ -59,6 +87,44 @@ Restated from A2A §4 so this document stands alone: 3.2 Conformance MUST be demonstrated by a **compliance report** (§13) generated against the A2A Technology Compatibility Kit (TCK) and this specification. A tool MUST NOT advertise a tier it has not demonstrated. +### 3.3 Requirement identifiers + +Each testable requirement carries a stable identifier of the form: + +``` +A2ACLI__ +``` + +where `` names the command or cross-cutting concern and `` is a zero-padded sequence number within that area — for example `A2ACLI_SEND_002`, `A2ACLI_CONV_001`, `A2ACLI_OUT_003`. + +Defined areas: + +| Area | Covers | Area | Covers | +| --- | --- | --- | --- | +| `DEFAULT` | Default behavior (§4.5) | `OUT` | Output & error contract (§9.1–9.4) | +| `DISCOVER` | Agent Card discovery (§8.1) | `EXIT` | Exit codes (§9.6) | +| `SEND` | Sending messages (§8.2) | `AUTH` | Authentication (§10) | +| `GET` | Task retrieval (§8.3) | `TX` | Transport selection (§11.1) | +| `CANCEL` | Task cancellation (§8.4) | `VER` | Protocol versioning (§11.2, §11.3) | +| `LIST` | Task listing (§8.5) | `SKILL` | Agent skill descriptor (§12) | +| `SUB` | Subscription / streaming (§7.4, §8.5) | `CHAT` | Interactive session (§8.5) | +| `CONV` | Conversation & session state (§6) | `CONFIG` | Profiles / environments (§8.5) | +| `POLL` | Task status polling (§7) | `DOWNLOAD` | Artifact retrieval (§8.5) | +| `PUSH` | Push notifications (Tier 3) | `CONFORM` | TCK conformance check (§8.5) | +| `SERVE` | Local agent mode (Tier 3) | | | + +`ERR` is **reserved** and is never used as a requirement area: `A2ACLI_ERR_*` identifiers denote **error codes** (Appendix E), which carry a symbolic suffix rather than a number. Requirements about error handling live under `OUT`. + +Stability rules — these make the identifiers safe to cite in tooling, test suites, and compliance reports: + +- An identifier, once published, is **permanent**. It MUST NOT be renumbered, reused, or reassigned to a different requirement. +- **Tier membership is not encoded in the identifier.** A requirement may move between tiers across specification versions while keeping its identifier. +- New requirements take the next unused number in their area. Numbers need not be contiguous. +- A withdrawn requirement MUST be marked `Withdrawn` in the registry rather than deleted, and its number MUST NOT be reused. +- New areas MAY be added; existing area names MUST NOT be repurposed. + +The authoritative list of requirement identifiers is the compliance-report template published alongside this specification. + --- ## 4. Design principles @@ -249,9 +315,13 @@ A conformant tool MUST support **both** machine-readable modes. They serve diffe 9.4 Errors MUST be machine-readable in both modes (the error envelope in Appendix B) and MUST be normalized across transports so that the same A2A error yields the same tool-level result regardless of binding. In `jsonl`, an error terminating the stream MUST be emitted as a final error object on its own line. +The `code` field MUST carry a stable, symbolic error identifier of the form `A2ACLI_ERR_`, drawn from the registry in Appendix E. Symbolic codes — rather than bare numbers — let a caller match on meaning, keep working as the registry grows, and read clearly in logs. + +Only the **eight core codes** in Appendix E.1 are required. The extended codes in E.2 are optional refinements for tools that can tell those cases apart; a tool that cannot classify a condition MUST fall back to the applicable core code (`A2ACLI_ERR_INTERNAL` if none is closer). Tools MUST NOT invent codes in the `A2ACLI_ERR_` namespace; vendor-specific codes MUST use a distinct prefix. + 9.5 When the caller does not wait for completion (`--async` / `--return-immediately` / `--no-wait`), the tool MUST still emit a result object carrying the identifiers required to resume or poll later — at minimum `taskId` and `contextId` (§6.3) — so the caller can query status with `get` at a later time. -9.6 Exit codes: +9.6 Exit codes. Every error code in Appendix E maps to exactly one of these: the exit code is the coarse signal for shells and CI, the error code the precise one for programmatic callers. | Code | Meaning | | --- | --- | @@ -364,7 +434,7 @@ In a machine-readable mode (`json` or `jsonl`), task-affecting commands (`send`, } } ``` -- `code` — REQUIRED, the normalized CLI error identifier (stable across transports). +- `code` — REQUIRED, a symbolic `A2ACLI_ERR_` identifier from Appendix E (stable across transports). - `message` — REQUIRED, human-readable. - `a2aCode` — the underlying A2A/transport error code when one exists, else `null`. @@ -386,6 +456,48 @@ While the specification is in Draft, notable revisions are recorded by date; the | Version | Date | Notes | | --- | --- | --- | +| 0.1 (Draft) | 2026-08-10 | Restructured "Why this matters" into three sourced problems, adding testing a running agent as a distinct motivation. Split the error registry into eight required core codes (E.1) and optional extended refinements (E.2) so conformance stays cheap. Added the requirement-identifier convention (§3.3) and a symbolic error vocabulary. | | 0.1 (Draft) | 2026-08-06 | Defined two machine-readable output modes — `json` (exactly one buffered document) and `jsonl` (one object per line, flushed as produced) — and required both (§9.3). Renamed NDJSON to JSONL throughout. Added `--no-wait` as an alias for `--async` / `--return-immediately`, and required that not waiting still returns `taskId` and `contextId` for later polling (§9.5). | | 0.1 (Draft) | 2026-08-04 | Made governance implementation-neutral: removed the designation of a specific reference implementation and language from §15.3, and aligned §1.4, §14 and §15.2. | | 0.1 (Draft) | 2026-08-04 | Initial draft. Tier 1 normative; Tiers 2–3 outlined. Client-only baseline; conversation/session state (§6) and task polling (§7) as first-class; opinionated defaults (§4.5); conformant-vs-official + governance (§1.4, §15); normative output envelope (Appendix B). | + +## Appendix E — Error code registry (normative) + +Values for the `code` field of the error envelope (Appendix B). + +The registry is split so that conformance stays cheap: a tool needs only the **core** codes below. The **extended** codes exist for tools that can tell failures apart more precisely — using them is encouraged but never required, and a tool that cannot distinguish a case simply reports the core code instead. + +Identifiers are permanent: once published, a code MUST NOT be reused or redefined. Codes MAY be added later; a consumer MUST tolerate an unrecognized `A2ACLI_ERR_*` value and SHOULD fall back to the exit code. + +### E.1 Core codes (required) + +A conformant tool MUST be able to emit these. Together they cover every exit code, so a caller can always act on the result. + +| Code | Meaning | Exit | +| --- | --- | --- | +| `A2ACLI_ERR_USAGE` | Invalid arguments, flags, or flag combination | 2 | +| `A2ACLI_ERR_UNREACHABLE` | Agent could not be reached — DNS, connection, TLS, or no Agent Card | 3 | +| `A2ACLI_ERR_AUTH_REQUIRED` | Credentials required but not supplied | 4 | +| `A2ACLI_ERR_AUTH_FAILED` | Credentials supplied but rejected | 4 | +| `A2ACLI_ERR_TASK_FAILED` | Task ended unsuccessfully (`FAILED`, or `REJECTED` if not distinguished) | 5 | +| `A2ACLI_ERR_INPUT_REQUIRED` | Task needs caller input in a non-interactive run | 6 | +| `A2ACLI_ERR_TIMEOUT` | `--timeout` expired before a terminal state | 7 | +| `A2ACLI_ERR_INTERNAL` | Unexpected tool-side failure, or any condition with no better code | 1 | + +### E.2 Extended codes (optional) + +Use when the tool can distinguish the case. Each refines a core code; if unsupported, report the core code shown in brackets. + +| Code | Meaning | Refines | Exit | +| --- | --- | --- | --- | +| `A2ACLI_ERR_CARD_NOT_FOUND` | No Agent Card at the well-known location or given URL | [`UNREACHABLE`] | 3 | +| `A2ACLI_ERR_CARD_INVALID` | Agent Card fetched but malformed or schema-invalid | [`INTERNAL`] | 1 | +| `A2ACLI_ERR_TASK_NOT_FOUND` | Referenced `taskId` does not exist | [`INTERNAL`] | 1 | +| `A2ACLI_ERR_TASK_REJECTED` | Task reached `REJECTED` rather than `FAILED` | [`TASK_FAILED`] | 5 | +| `A2ACLI_ERR_TASK_NOT_CANCELABLE` | Task cannot be canceled in its current state | [`INTERNAL`] | 1 | +| `A2ACLI_ERR_CONTEXT_MISMATCH` | `contextId` and `taskId` do not correspond | [`INTERNAL`] | 1 | +| `A2ACLI_ERR_CAPABILITY_UNSUPPORTED` | Agent does not advertise a required capability (e.g. streaming) | [`INTERNAL`] | 1 | +| `A2ACLI_ERR_TRANSPORT_UNSUPPORTED` | No transport in common between tool and Agent Card | [`INTERNAL`] | 1 | +| `A2ACLI_ERR_VERSION_UNSUPPORTED` | Agent rejected the signaled protocol version | [`INTERNAL`] | 1 | +| `A2ACLI_ERR_EXTENSION_REQUIRED` | Agent requires an extension the tool does not support | [`INTERNAL`] | 1 | +| `A2ACLI_ERR_STREAM_INTERRUPTED` | Stream ended before a terminal state and could not be resumed | [`INTERNAL`] | 1 | From e5a6811f6ba2f1cd3ca7b3eab1dfd87f5ba12a03 Mon Sep 17 00:00:00 2001 From: Sampath Kumar Date: Mon, 10 Aug 2026 11:40:15 +0000 Subject: [PATCH 06/31] Add an actionable hint field to the error envelope Field evidence: across the A2A CLIs surveyed, the most useful thing an error carries is not its code but a suggested next step. ghchinoy/a2acli derives hints from the Agent Card's security schemes and emits a copy-pasteable command, e.g. "Run: a2acli auth login -u ". The wider CLI world agrees: the Command Line Interface Guidelines ask implementers to catch errors and rewrite them for humans, and tools with a single exit code lean entirely on message quality. The draft specified a precise code vocabulary and said nothing about guidance, optimising the axis with the least evidence behind it. The error envelope now carries a RECOMMENDED hint alongside code and message: a code tells a program what happened, a hint tells a person what to do about it. --- specification/SPEC.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/specification/SPEC.md b/specification/SPEC.md index 7bf9646..8dfa989 100644 --- a/specification/SPEC.md +++ b/specification/SPEC.md @@ -319,6 +319,8 @@ The `code` field MUST carry a stable, symbolic error identifier of the form `A2A Only the **eight core codes** in Appendix E.1 are required. The extended codes in E.2 are optional refinements for tools that can tell those cases apart; a tool that cannot classify a condition MUST fall back to the applicable core code (`A2ACLI_ERR_INTERNAL` if none is closer). Tools MUST NOT invent codes in the `A2ACLI_ERR_` namespace; vendor-specific codes MUST use a distinct prefix. +A tool SHOULD also populate the envelope's `hint` field with an actionable next step (Appendix B). A precise code tells a program what happened; a good hint tells a person what to do about it, and costs far less to implement than the rest of this section. + 9.5 When the caller does not wait for completion (`--async` / `--return-immediately` / `--no-wait`), the tool MUST still emit a result object carrying the identifiers required to resume or poll later — at minimum `taskId` and `contextId` (§6.3) — so the caller can query status with `get` at a later time. 9.6 Exit codes. Every error code in Appendix E maps to exactly one of these: the exit code is the coarse signal for shells and CI, the error code the precise one for programmatic callers. @@ -430,12 +432,14 @@ In a machine-readable mode (`json` or `jsonl`), task-affecting commands (`send`, "error": { "code": "string", "message": "string", + "hint": "string | null", "a2aCode": "string | number | null" } } ``` - `code` — REQUIRED, a symbolic `A2ACLI_ERR_` identifier from Appendix E (stable across transports). - `message` — REQUIRED, human-readable. +- `hint` — RECOMMENDED. A short, actionable next step, ideally a copy-pasteable command. Derive it from context where possible — for example, reading the Agent Card's security schemes to name the exact login command for that agent. Omit it, or set `null`, when there is nothing useful to say; never pad it. - `a2aCode` — the underlying A2A/transport error code when one exists, else `null`. **Streaming (`--output jsonl`):** one JSON object per line, flushed as produced. Each line MUST carry a `type` field identifying the event (for example `status`, `artifact`, `result`, `error`), and the final/terminal line MUST include the task-operation fields above so a reader that keeps only the last line still obtains `taskId`, `contextId`, and `state`. @@ -456,7 +460,7 @@ While the specification is in Draft, notable revisions are recorded by date; the | Version | Date | Notes | | --- | --- | --- | -| 0.1 (Draft) | 2026-08-10 | Restructured "Why this matters" into three sourced problems, adding testing a running agent as a distinct motivation. Split the error registry into eight required core codes (E.1) and optional extended refinements (E.2) so conformance stays cheap. Added the requirement-identifier convention (§3.3) and a symbolic error vocabulary. | +| 0.1 (Draft) | 2026-08-10 | Added a RECOMMENDED `hint` field to the error envelope (Appendix B, §9.4) — an actionable next step alongside the machine-readable code. Restructured "Why this matters" into three sourced problems, adding testing a running agent as a distinct motivation. Split the error registry into eight required core codes (E.1) and optional extended refinements (E.2) so conformance stays cheap. Added the requirement-identifier convention (§3.3) and a symbolic error vocabulary. | | 0.1 (Draft) | 2026-08-06 | Defined two machine-readable output modes — `json` (exactly one buffered document) and `jsonl` (one object per line, flushed as produced) — and required both (§9.3). Renamed NDJSON to JSONL throughout. Added `--no-wait` as an alias for `--async` / `--return-immediately`, and required that not waiting still returns `taskId` and `contextId` for later polling (§9.5). | | 0.1 (Draft) | 2026-08-04 | Made governance implementation-neutral: removed the designation of a specific reference implementation and language from §15.3, and aligned §1.4, §14 and §15.2. | | 0.1 (Draft) | 2026-08-04 | Initial draft. Tier 1 normative; Tiers 2–3 outlined. Client-only baseline; conversation/session state (§6) and task polling (§7) as first-class; opinionated defaults (§4.5); conformant-vs-official + governance (§1.4, §15); normative output envelope (Appendix B). | From e51bb7dbeb8adf87806d6e71a5c27b4cb74b0cbb Mon Sep 17 00:00:00 2001 From: Sampath Kumar Date: Mon, 10 Aug 2026 21:57:15 +0200 Subject: [PATCH 07/31] Update specification/SPEC.md Co-authored-by: Sam Betts <1769706+Tehsmash@users.noreply.github.com> --- specification/SPEC.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/SPEC.md b/specification/SPEC.md index 8dfa989..512be44 100644 --- a/specification/SPEC.md +++ b/specification/SPEC.md @@ -53,7 +53,7 @@ References of the form "A2A §x" point to the A2A Protocol Specification v1.0. W 1.1 An `a2a-cli` is an **A2A client**: it initiates requests to an A2A server (a remote agent) and renders the responses. Acting *as* a server — publishing an Agent Card, generating server-side identifiers, or serving inbound requests — is **outside the baseline** of this specification (see §8.5, optional). -1.2 The primary purpose of an `a2a-cli` is **conversation with an A2A server**. A2A conversations are multi-turn and stateful and typically span multiple CLI invocations. A conformant tool MUST therefore allow a caller to **start, continue, inspect, and resume** a conversation (§6), and MUST provide a **polling** path for task status in addition to any streaming support (§7). +1.2 The primary purpose of an `a2a-cli` is **interaction with an A2A server**. A2A interactions MAY be multi-turn and MAY be stateful and spanning multiple CLI invocations. A conformant tool MUST therefore allow a caller to **start, continue, inspect, and resume** a conversation (§6), and MUST provide a **polling** path for task status in addition to any streaming support (§7). 1.3 Conformance is **tiered and evidence-based** (§3). A tool asserts conformance by publishing a compliance report. Any number of **conformant** tools MAY coexist; conformance is open to any implementation, in any language, that passes the specification. From 56a9dff00bc246cd240038cfa08e6f7e9eaadb41 Mon Sep 17 00:00:00 2001 From: Sampath Kumar Date: Tue, 11 Aug 2026 00:38:11 +0200 Subject: [PATCH 08/31] Update specification/SPEC.md Co-authored-by: Sam Betts <1769706+Tehsmash@users.noreply.github.com> --- specification/SPEC.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/SPEC.md b/specification/SPEC.md index 512be44..354c079 100644 --- a/specification/SPEC.md +++ b/specification/SPEC.md @@ -215,7 +215,7 @@ A conformant tool MUST allow continuation via explicit options: Rules: - When `--task-id` is supplied, the tool MUST send the message against that task and MUST surface any server error (e.g. not-found or state conflict) rather than silently starting a new task. -- When only `--context-id` is supplied, the tool starts a new task grouped under that conversation. +- When only `--context-id` is supplied, the tool sends a message under that context which MAY return a message or Task - When both are supplied, the tool MUST pass them through unchanged. - Interactive `chat` (Tier 2) MUST carry the `contextId` — and the active `taskId` while a task is interrupted — across turns automatically. From 5c11399a976f7a7a58c2b2ffd90756db524207f2 Mon Sep 17 00:00:00 2001 From: Sampath Kumar Date: Tue, 11 Aug 2026 13:01:29 +0000 Subject: [PATCH 09/31] Revise the specification following external review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Terminology. Interaction, not conversation: A2A has no concept of a conversation, and the contextId definition overstated things by implying one. What the grouping means is the agent author's decision. Transport. supported_interfaces is always in server preference order, so the specification described a state that cannot occur — a card with no decisive preference. --transport becomes repeatable and ordered, because a single value leaves a client unable to negotiate against a card that does not offer it. Version negotiation now says what to do rather than only what not to: negotiate down to the highest mutually supported version, never below 1.0. Output. Appendix B defined a schema duplicating types the protocol already has; SendMessageResponse and StreamResponse are the discriminated unions it was reaching for. It now names which protocol type each command emits and defines nothing itself. --output json emits the terminal object rather than a buffered event log, bounding its size by the task. Framing is never switched implicitly: a caller that asked for json and received jsonl parses the first line and returns a confidently wrong answer. The tui mode is gone, and 9.2 now pins what the default text mode must satisfy, which also resolves the conflict where 4.1's "structured output" was read as machine-parseable when it meant predictable. State. The CLI is stateless. Identifiers are reported in output and supplied as input; nothing is carried between invocations. 6.4 had conflated session state with configuration, which is what made the persistence question hard to answer; only configuration survives, with a documented precedence and scoping by agent-card reference. Commands. get and list become task get and task list, so adding context or message listing later is not a breaking change. discover becomes agent-inspect, since discovery already names the process of resolving a card. --service-url and --card-url — which contradicted each other across sections — become --agent-card, accepting a host, an explicit URL, or a file:// path, so offline conformance runs no longer need a live server. --verbose and --debug separate presentation from logging. Message parts gain the detail they lacked: multiple parts, stdin via --data -, and an explicit media type, because file extensions are unreliable and often absent. Errors. Protocol failures carry the A2A error by name per A2A 3.3.2 with the 5.4 mappings, rather than 11.6, which covers one binding only and would contradict 4.4. Appendix E keeps only conditions the protocol has no opinion on and drops from nineteen codes to ten. Conformance. 3.2 required a report generated against the A2A TCK. The TCK validates agents, not clients, so this asked for something a CLI cannot do. Conformance is now demonstrated against a live agent with an outcome per requirement identifier, and the TCK's role is upstream: confirming the agent under test is itself conformant, so a failure is attributable to the CLI. A requirement that cannot be provoked is recorded with its reason and never assumed to pass. Identifiers. Areas renamed to match the commands they cover: INSPECT, INTERACT, TASK_GET, TASK_CANCEL, TASK_LIST. Reuse is forbidden at every status; renumbering is permitted while Draft and frozen from the first Proposed version. Tiers and scope. chat moves to Tier 3 as a layer on top of the basic operations; push-notification config drops to Tier 2, since only hosting the receiver is the hard part. Shipping an agent skill becomes conditional, and 12.4 replaces a distribution rule no implementation could satisfy — no agent-facing standard defines an installation mechanism. Output checks consolidate from seven to six, Tier 1 from twenty-nine requirements to twenty-eight. Editorial. Why this matters merges to two problems, framing the CLI as a lightweight testing framework alongside the existing SDKs, TCK and samples. Command tables sort by tier then name. Appendix C separates normative from informative references and drops entries nothing cites. Appendix D records the revision. --- specification/COMPLIANCE.md | 81 +++++---- specification/README.md | 8 +- specification/SPEC.md | 341 +++++++++++++++++++----------------- 3 files changed, 237 insertions(+), 193 deletions(-) diff --git a/specification/COMPLIANCE.md b/specification/COMPLIANCE.md index 6cb0bc4..0792331 100644 --- a/specification/COMPLIANCE.md +++ b/specification/COMPLIANCE.md @@ -2,7 +2,7 @@ > **Template.** Copy this file into your tool's repository, complete every field, and replace all `<…>` placeholders. Link the finished report from the A2A compatibility matrix. A tool MUST NOT advertise a tier it has not demonstrated here. -This file is also the **authoritative registry of requirement identifiers** (`SPEC.md` §3.3). Identifiers are permanent: once published, an identifier is never renumbered, reused, or reassigned. Tier membership is *not* encoded in the identifier, so a requirement can move tiers between specification versions and still be tracked by the same ID. Withdrawn requirements stay listed, marked `Withdrawn`, and their numbers are never reused. +This file is also the **authoritative registry of requirement identifiers** (`SPEC.md` §3.3). An identifier is never **reused** — a retired number never returns meaning something else — but **renumbering is permitted while the specification is a Draft** and freezes from the first Proposed version. Tier membership is *not* encoded in the identifier, so a requirement can move tiers and still be tracked by the same ID. From Proposed onward, withdrawn requirements stay listed and marked `Withdrawn`. **This list is not fixed — it is expected to grow.** If you are building a tool and hit a real use case that no requirement covers, please open a request against the specification repository. It can be added in a future revision rather than left undocumented. Adding requirements never changes existing identifiers, so reports and test suites that cite them keep working. @@ -12,6 +12,8 @@ This file is also the **authoritative registry of requirement identifiers** (`SP **On exit codes.** An exit code is the number a command hands back to the shell when it finishes: `0` means success, anything else signals a failure. It matters because it is the only result a script or CI job gets without parsing output — `a2a-cli send … && deploy.sh` behaves correctly only if the tool exits non-zero when the task actually failed. The check confirms the tool returns a meaningful status rather than always `0`, and that the status agrees with the error it reported. +**On errors.** Failures come in two layers (`SPEC.md` §9.4): a protocol failure carries the A2A error by name (A2A §3.3.2, mapped per §5.4), and a CLI-local failure carries an `A2ACLI_ERR_*` identifier from Appendix E. A tool that renames protocol errors into a vocabulary of its own does not satisfy `A2ACLI_OUT_004`. + ## 1. Summary | Field | Value | @@ -25,7 +27,8 @@ This file is also the **authoritative registry of requirement identifiers** (`SP | Specification version targeted | `0.1` | | **Tier claimed** | `` | | A2A protocol version(s) | `` | -| A2A TCK version(s) tested | `` | +| Agent exercised against | `` | +| That agent is TCK-conformant? | `` | | Transports covered | `` | | Designation | `` | @@ -44,7 +47,7 @@ Every `◐`, `❌`, or `—` MUST carry a note. | Tier | Requirements | `✅` | `◐` | `❌` | `—` | Tier satisfied? | | --- | --- | --- | --- | --- | --- | --- | -| Tier 1 — Core | 29 | `<>` | `<>` | `<>` | `<>` | `` | +| Tier 1 — Core | 28 | `<>` | `<>` | `<>` | `<>` | `` | | Tier 2 — Standard | 13 | `<>` | `<>` | `<>` | `<>` | `` | | Tier 3 — Advanced | 11 | `<>` | `<>` | `<>` | `<>` | `` | @@ -54,68 +57,82 @@ A tier is satisfied only when every requirement in it is `✅`. Tiers are cumula | ID | Requirement | Spec § | Status | Notes | | --- | --- | --- | --- | --- | -| `A2ACLI_DISCOVER_001` | Fetch & parse an Agent Card from the well-known location or an explicit URL | §8.1 | `<>` | | -| `A2ACLI_SEND_001` | Send a message to start a conversation | §8.2 | `<>` | | +| `A2ACLI_INSPECT_001` | `agent-inspect` — resolve and parse an Agent Card from a host, an explicit URL, or a `file://` path | §8.1 | `<>` | | +| `A2ACLI_SEND_001` | Send a message to start an interaction | §8.2 | `<>` | | | `A2ACLI_SEND_002` | Blocking by default; `--async` / `--return-immediately` / `--no-wait` overrides | §8.2, §4.5 | `<>` | | | `A2ACLI_SEND_003` | `--stream` consumes SSE when supported; never hangs when unsupported | §8.2, §7.2 | `<>` | | | `A2ACLI_SEND_004` | Renders produced artifacts | §8.2 | `<>` | | -| `A2ACLI_GET_001` | Retrieve task state, artifacts, and history by identifier | §8.3 | `<>` | | -| `A2ACLI_CANCEL_001` | Cancel a task; idempotent | §8.4 | `<>` | | -| `A2ACLI_CONV_001` | Continue a conversation via `--context-id` | §6.2 | `<>` | | -| `A2ACLI_CONV_002` | Continue a task via `--task-id`; never silently starts a new task | §6.2 | `<>` | | -| `A2ACLI_CONV_003` | Never invents server-assigned identifiers | §6.1 | `<>` | | -| `A2ACLI_CONV_004` | Reports `taskId` / `contextId` / `state` on completion and interruption | §6.3 | `<>` | | -| `A2ACLI_CONV_005` | Persisted session state *(if any)*: conventional path, secrets not world-readable, explicit flags win | §6.4 | `<>` | | +| `A2ACLI_TASK_GET_001` | `task get` — retrieve task state, artifacts, and history by identifier | §8.3 | `<>` | | +| `A2ACLI_TASK_CANCEL_001` | `task cancel` — cancel a task; idempotent | §8.4 | `<>` | | +| `A2ACLI_INTERACT_001` | Continue an interaction via `--context-id` | §6.2 | `<>` | | +| `A2ACLI_INTERACT_002` | Continue a task via `--task-id`, which MUST be accompanied by `--context-id`; a rejected identifier warns and continues in the same context, reporting both identifiers | §6.2 | `<>` | | +| `A2ACLI_INTERACT_003` | Never invents server-assigned identifiers | §6.1 | `<>` | | +| `A2ACLI_INTERACT_004` | Reports `taskId` / `contextId` / `state` on completion and interruption | §6.3 | `<>` | | +| `A2ACLI_INTERACT_005` | Persisted configuration *(if any)*: conventional path, secrets not world-readable, documented precedence | §6.4 | `<>` | | | `A2ACLI_POLL_001` | Polling path available — `get` plus wait/watch | §7.3 | `<>` | | | `A2ACLI_POLL_002` | `--poll-interval` and `--timeout` honored; bounded backoff; no busy-loop | §7.3 | `<>` | | | `A2ACLI_POLL_003` | Stops immediately on interrupted states (`INPUT_REQUIRED` / `AUTH_REQUIRED`) without deadlock | §7.1, §7.3 | `<>` | | | `A2ACLI_POLL_004` | Falls back to polling on stream failure and reconciles final state with `get` | §7.3 | `<>` | | -| `A2ACLI_OUT_001` | **Standard response** — every response about a task says which conversation and which task it concerns, and what state it is in (`contextId`, `taskId`, `state`), in every output mode | §6.3, §9.5, Appendix B | `<>` | | -| `A2ACLI_OUT_002` | `--output json` — exactly one complete document, buffered even when the interaction streams | §9.3 | `<>` | | -| `A2ACLI_OUT_003` | `--output jsonl` — one complete JSON object per line, flushed as produced | §9.3 | `<>` | | -| `A2ACLI_OUT_004` | Stream discipline — payload on stdout, diagnostics on stderr, never mixed; degrades cleanly when stdout is not a terminal | §9.1, §9.2 | `<>` | | -| `A2ACLI_OUT_005` | Errors are machine-readable, consistent across transports, and carry one of the eight core `A2ACLI_ERR_*` codes (extended codes optional) | §9.4, Appendix E.1 | `<>` | | +| `A2ACLI_OUT_001` | **Standard output** — every response about a task says which context and which task it concerns and what state it is in, in every output mode; the payload goes to stdout and diagnostics to stderr, never mixed | §6.3, §9.1, §9.2, §9.5 | `<>` | | +| `A2ACLI_OUT_002` | `--output json` — exactly one document, the **terminal** protocol object rather than an event log, and never switched implicitly to `jsonl` | §9.3, App. B | `<>` | | +| `A2ACLI_OUT_003` | `--output jsonl` — one complete JSON object per line, flushed as produced | §9.3, App. B | `<>` | | +| `A2ACLI_OUT_004` | Errors are machine-readable and consistent across transports: protocol failures carry the A2A error name, CLI-local failures an `A2ACLI_ERR_*` code | §9.4, App. E | `<>` | | | `A2ACLI_EXIT_001` | Exit status follows the documented scheme and matches the error code reported | §9.6, Appendix E | `<>` | | -| `A2ACLI_AUTH_001` | Scriptable credentials — bearer, API key, custom header, env equivalents | §10.1 | `<>` | | +| `A2ACLI_AUTH_001` | Scriptable credentials — bearer, API key, env equivalents, attached as service parameters; `-H/--header` available separately for any service parameter | §10.1 | `<>` | | | `A2ACLI_TX_001` | Transport selected from the Agent Card, honoring declared preference | §11.1 | `<>` | | -| `A2ACLI_TX_002` | Defaults to HTTP+JSON when the card expresses no decisive preference | §11.1, §4.5 | `<>` | | -| `A2ACLI_VER_001` | Protocol version signaled explicitly on every request; no silent downgrade | §11.2 | `<>` | | -| `A2ACLI_DEFAULT_001` | Ships the baseline defaults, each overridable by an explicit flag | §4.5 | `<>` | | -| `A2ACLI_SKILL_001` | Ships exactly one skill descriptor, generic and token-efficient, deferring to runtime `help` rather than inlining the command surface | §12.1, §12.2 | `<>` | | -| `A2ACLI_SKILL_002` | Skill and specification kept as distinct layers; the skill does not restate normative requirements | §12.3 | `<>` | | +| `A2ACLI_TX_002` | Uses the first `supported_interfaces` entry it supports absent a client preference; `--transport` is repeatable and ordered | §11.1, §4.5 | `<>` | | +| `A2ACLI_VER_001` | Protocol version signaled explicitly on every request; negotiates down only within 1.x, never below 1.0; no silent downgrade | §11.2 | `<>` | | +| `A2ACLI_DEFAULT_001` | Ships the baseline defaults, each overridable by an explicit flag. **Complete the breakdown below** — a bare pass hides which default is missing | §4.5 | `<>` | | +| `A2ACLI_SKILL_001` | *(Conditional — mark `— N/A` if the tool ships no skill.)* Ships **exactly one** Agent Skill, generic and token-efficient, deferring to runtime `help` rather than inlining the command surface | §12.1, §12.2 | `<>` | | +| `A2ACLI_SKILL_002` | *(Conditional — mark `— N/A` if the tool ships no skill.)* Skill and specification kept as distinct layers; the skill does not restate normative requirements | §12.3 | `<>` | | + +### 4a. `A2ACLI_DEFAULT_001` breakdown + +`DEFAULT_001` passes only when every row is present and overridable. Report each one; "mostly defaults" is not a result anyone can act on. + +| Default (§4.5) | Shipped? | Overridable by flag? | Notes | +| --- | --- | --- | --- | +| Transport — server preference order | `<>` | `<>` | | +| Task completion — wait by default | `<>` | `<>` | | +| Output — human-readable `text` | `<>` | `<>` | | +| Detail level — concise | `<>` | `<>` | | +| Protocol version — highest mutually supported, never below 1.0 | `<>` | `<>` | | +| Transport security — TLS verification on | `<>` | `<>` | | ## 5. Tier 2 — Standard | ID | Requirement | Spec § | Status | Notes | | --- | --- | --- | --- | --- | -| `A2ACLI_LIST_001` | `list` — cursor-paginated, filterable by status and context | §8.5 | `<>` | | +| `A2ACLI_TASK_LIST_001` | `task list` — cursor-paginated, filterable by status and context | §8.5 | `<>` | | | `A2ACLI_SUB_001` | `subscribe` — (re)subscribe to a task's event stream | §8.5 | `<>` | | | `A2ACLI_SUB_002` | Stream resumption after disconnect, reconciled with `get` | §7.4 | `<>` | | | `A2ACLI_AUTH_002` | `auth login` — OAuth 2.1 device-code flow | §10.2 | `<>` | | | `A2ACLI_AUTH_003` | `auth login` — OAuth 2.1 client-credentials flow | §10.2 | `<>` | | | `A2ACLI_AUTH_004` | Secure token storage with automatic attachment on later calls | §10.2 | `<>` | | | `A2ACLI_TX_003` | At least two transports supported and selectable | §11.1 | `<>` | | -| `A2ACLI_CONFIG_001` | Named environments / config profiles (`--env`) | §6.4, §8.5 | `<>` | | -| `A2ACLI_CHAT_001` | Interactive `chat` carrying context and task across turns | §6.2, §8.5 | `<>` | | +| `A2ACLI_CONFIG_001` | Configuration precedence (flag → env → local → global → built-in), scopeable by agent-card reference | §6.4, §8.5 | `<>` | | + | `A2ACLI_DOWNLOAD_001` | `download` — save task artifacts to disk | §8.5 | `<>` | | -| `A2ACLI_OUT_006` | Wire debug (`--dump-wire`) emits raw protocol JSON to stderr | §8.5 | `<>` | | +| `A2ACLI_OUT_005` | `--debug` enables diagnostic logging; `--dump-wire` emits raw protocol JSON to stderr | §8.5 | `<>` | | | `A2ACLI_CONFORM_001` | `conformance` — smoke-check a live agent against the A2A TCK | §8.5 | `<>` | | -| `A2ACLI_OUT_007` | Shell completions provided | §8.5 | `<>` | | +| `A2ACLI_OUT_006` | Shell completions provided | §8.5 | `<>` | | +| `A2ACLI_PUSH_001` | `push-config` create / get / list / delete | §8.5 | `<>` | | ## 6. Tier 3 — Advanced | ID | Requirement | Spec § | Status | Notes | | --- | --- | --- | --- | --- | -| `A2ACLI_PUSH_001` | `push-config` create / get / list / delete | §8.5 | `<>` | | + | `A2ACLI_PUSH_002` | Local webhook receiver able to accept push notifications | §8.5, §7.2 | `<>` | | +| `A2ACLI_CHAT_001` | Interactive `chat` carrying context and task across turns | §6.2, §8.5 | `<>` | | | `A2ACLI_TX_004` | gRPC transport | §11.1 | `<>` | | -| `A2ACLI_DISCOVER_002` | Authenticated extended Agent Card | §8.5, §10.4 | `<>` | | -| `A2ACLI_DISCOVER_003` | Agent Card signature verification | §8.5 | `<>` | | +| `A2ACLI_INSPECT_002` | Authenticated extended Agent Card | §8.5, §10.4 | `<>` | | +| `A2ACLI_INSPECT_003` | Agent Card signature verification | §8.5 | `<>` | | | `A2ACLI_AUTH_005` | Mutual TLS | §10.3 | `<>` | | | `A2ACLI_AUTH_006` | OpenID Connect | §10.3 | `<>` | | | `A2ACLI_AUTH_007` | Handles in-task `AUTH_REQUIRED` resolution | §10.3 | `<>` | | | `A2ACLI_SERVE_001` | `serve` / mock agent mode | §8.5 | `<>` | | -| `A2ACLI_DISCOVER_004` | Catalog / registry integration | §8.5 | `<>` | | +| `A2ACLI_INSPECT_004` | Catalog / registry integration | §8.5 | `<>` | | | `A2ACLI_VER_002` | Declares server-required protocol extensions | §11.3 | `<>` | | ## 7. Test evidence diff --git a/specification/README.md b/specification/README.md index 6c983ff..b55bfdc 100644 --- a/specification/README.md +++ b/specification/README.md @@ -2,7 +2,7 @@ This directory holds the specification for **`a2a-cli`** — a command-line client for the [Agent2Agent (A2A) Protocol](https://a2a-protocol.org/latest/specification/) — together with the compliance-report template used to demonstrate conformance. -`a2a-cli` lets developers and AI coding agents discover, message, stream, poll, and inspect A2A agents from the terminal. The specification defines the behavior a tool must exhibit so that independently built CLIs — in any language — converge on one predictable command surface, output contract, and conversation model, verifiable through a published compliance report. +`a2a-cli` lets developers and AI coding agents inspect, message, stream, poll, and manage A2A agents from the terminal. The specification defines the behavior a tool must exhibit so that independently built CLIs — in any language — converge on one predictable command surface, output contract, and interaction model, verifiable through a published compliance report. ## Contents @@ -17,9 +17,9 @@ Conformance is tiered and cumulative; a tier is satisfied only when every MUST i | Tier | Name | Summary | | --- | --- | --- | -| **Tier 1** | Core | Discover, send, get, cancel; multi-turn conversation state; task polling; structured output + exit codes; token auth; version signaling; a `SKILL.md`. | -| **Tier 2** | Standard | Task listing, streaming subscribe, OAuth login, multiple transports, config profiles, interactive chat, artifact download, wire debug, conformance check, completions. | -| **Tier 3** | Advanced | Push notifications + webhook receiver, gRPC, extended card, signature verification, mTLS, OpenID Connect, serve/mock, catalog, extensions. | +| **Tier 1** | Core | `agent-inspect`, `send`, `task get`, `task cancel`; explicit interaction state; task polling; the output contract + exit codes; token auth; version signaling. | +| **Tier 2** | Standard | Task listing, streaming subscribe, OAuth login, multiple transports, configuration precedence, push-notification config, artifact download, wire debug, conformance check, completions. | +| **Tier 3** | Advanced | Push-notification webhook receiver, interactive chat, gRPC, extended card, signature verification, mTLS, OpenID Connect, serve/mock, catalog, extensions. | See [`SPEC.md`](./SPEC.md) for the normative detail. diff --git a/specification/SPEC.md b/specification/SPEC.md index 354c079..c05c74b 100644 --- a/specification/SPEC.md +++ b/specification/SPEC.md @@ -2,38 +2,36 @@ **Version:** 0.1 **Status:** Draft — open for review. -**Last updated:** 2026-08-10 +**Last updated:** 2026-08-11 **Applies to:** A2A Protocol v1.0 ## Abstract -This document specifies the behavior that a command-line interface (CLI) tool MUST, SHOULD, and MAY exhibit to be considered a conformant **`a2a-cli`** — a terminal client for the [Agent2Agent (A2A) Protocol](https://a2a-protocol.org/latest/specification/). It exists so that independently built CLIs, in any language, converge on one predictable command surface, output contract, and conversation model — measurable through a published compliance report. +This document specifies the behavior that a command-line interface (CLI) tool MUST, SHOULD, and MAY exhibit to be considered a conformant **`a2a-cli`** — a terminal client for the [Agent2Agent (A2A) Protocol](https://a2a-protocol.org/latest/specification/). It exists so that independently built CLIs, in any language, converge on one predictable command surface, output contract, and interaction model — measurable through a published compliance report. This is an **implementer's specification**. Its audience is engineers building or improving an `a2a-cli`. It constrains CLI behavior only and never modifies A2A wire semantics. ## Why this matters -Three problems, all traceable to the same cause: there is no official A2A command-line tool. +Two problems, both traceable to the same cause: there is no official A2A command-line tool. ### 1. Everyone builds their own, and they build the same thing -At least seven independent A2A CLIs exist today, across six languages — Go, Rust, Python, TypeScript, .NET, and Swift. Two already sit inside the A2A project's own GitHub organization ([A2A#1929](https://github.com/a2aproject/A2A/issues/1929)). +At least eight independent A2A CLIs exist today, across six languages — Go, Rust, Python, TypeScript, .NET, and Swift. Two already sit inside the A2A project's own GitHub organization ([A2A#1929](https://github.com/a2aproject/A2A/issues/1929)). -They largely re-implement the same short list of operations: send a task, continue a multi-turn conversation, check task status, read artifacts. Those are exactly the operations an official tool should cover. +They largely re-implement the same short list of operations: send a task, continue a multi-turn interaction, check task status, read artifacts. Those are exactly the operations an official tool should cover. The request keeps recurring rather than resolving. An earlier CLI contribution was closed as out of scope for its repository ([PR#1323](https://github.com/a2aproject/A2A/pull/1323)); its follow-up issue was closed as a duplicate ([#1325](https://github.com/a2aproject/A2A/issues/1325)); a working command grammar was designed separately inside the Go SDK ([a2a-go#306](https://github.com/a2aproject/a2a-go/discussions/306)); and the consolidation request itself ([A2A#1929](https://github.com/a2aproject/A2A/issues/1929)) is open for community vote. -### 2. AI coding agents have no standard way in +### 2. Driving and testing a running agent has no standard path -Developers increasingly work through AI coding agents, and those agents drive tools from the terminal. An agent learns to use a command-line tool from a skill descriptor — so with no canonical CLI there can be no canonical skill file, and therefore no standard way for a coding agent to work with an A2A agent at all ([A2A#1929](https://github.com/a2aproject/A2A/issues/1929)). +A2A already provides SDKs in several languages, a Technology Compatibility Kit, an inspector, and a samples repository. What none of them gives is a quick way to exercise a *running* agent from outside whichever SDK you happen to be using — so checking that a deployed service actually behaves means writing client code first. -Agents also consume output directly. They need predictable, machine-readable results and stable exit codes rather than a screen formatted for humans, which is why machine-readable output (§9.3) and a skill descriptor (§12) are core requirements here rather than optional extras. +The same gap blocks AI coding agents, for a related reason. Developers increasingly work through them, and an agent learns a command-line tool from a skill descriptor. With no canonical CLI there can be no canonical skill file, and therefore no standard way for a coding agent to work with an A2A agent at all ([A2A#1929](https://github.com/a2aproject/A2A/issues/1929)). -### 3. Testing a running agent is harder than it should be +A conformant CLI closes both at once. Because it is scriptable, emits machine-readable output and returns meaningful exit codes, it doubles as a lightweight testing framework: a bash script or a Python test can drive real interactions against a live agent, and an agentic harness can do the same unattended. Testing an A2A service — by hand while developing, or automatically in CI — stops being a project and becomes a command. -A2A already provides SDKs in several languages, a Technology Compatibility Kit, an inspector, and a sample repository. What is missing is a quick way to exercise a *running* agent from outside any one SDK. - -A conformant CLI fills that gap. Because it is scriptable and emits machine-readable output, it doubles as a lightweight test harness: a shell script or a Python test can drive real conversations against a live agent, and a coding agent can do the same unattended. That lowers the cost of checking an A2A service — by hand during development, or automatically in CI — without writing client code first. +That is also why machine-readable output (§9.3) is a core requirement here rather than an optional extra: the tool's consumers are as often programs as people. ### What this document does about it @@ -53,7 +51,7 @@ References of the form "A2A §x" point to the A2A Protocol Specification v1.0. W 1.1 An `a2a-cli` is an **A2A client**: it initiates requests to an A2A server (a remote agent) and renders the responses. Acting *as* a server — publishing an Agent Card, generating server-side identifiers, or serving inbound requests — is **outside the baseline** of this specification (see §8.5, optional). -1.2 The primary purpose of an `a2a-cli` is **interaction with an A2A server**. A2A interactions MAY be multi-turn and MAY be stateful and spanning multiple CLI invocations. A conformant tool MUST therefore allow a caller to **start, continue, inspect, and resume** a conversation (§6), and MUST provide a **polling** path for task status in addition to any streaming support (§7). +1.2 The primary purpose of an `a2a-cli` is **interaction with an A2A server**. A2A interactions MAY be multi-turn and MAY be stateful, and MAY span multiple CLI invocations. A conformant tool MUST therefore allow a caller to **start, continue, inspect, and resume** an interaction (§6), and MUST provide a **polling** path for task status in addition to any streaming support (§7). 1.3 Conformance is **tiered and evidence-based** (§3). A tool asserts conformance by publishing a compliance report. Any number of **conformant** tools MAY coexist; conformance is open to any implementation, in any language, that passes the specification. @@ -67,10 +65,10 @@ References of the form "A2A §x" point to the A2A Protocol Specification v1.0. W Restated from A2A §4 so this document stands alone: -- **Message** — a single conversational turn. Has a `role` (`user` or `agent`) and one or more **Parts** (`text`, `file`, or `data`). Carries a client-assigned **`messageId`**. +- **Message** — a single turn. Has a `role` (`user` or `agent`) and one or more **Parts** (`text`, `file`, or `data`). Carries a client-assigned **`messageId`**. - **Task** — the stateful unit of work a message may create. Identified by a server-assigned **`taskId`**; advances through a **TaskState** (§7.1); may emit **Artifacts**. - **Artifact** — a task output (text, structured data, or file). Task outputs are delivered as Artifacts, not Messages; a conformant tool MUST render Artifacts. -- **`contextId`** — a server-assigned, opaque identifier that groups related tasks and messages into a single **conversation**. +- **`contextId`** — a server-assigned, opaque identifier that groups related tasks and messages. A2A does not define what the grouping *means*; that is the agent author's decision, and a conformant tool MUST NOT assume it denotes a chat session. - **AgentCard** — the server's machine-readable description of identity, capabilities, interfaces (transports), security schemes, and skills, obtained during discovery (§8.1). --- @@ -81,11 +79,13 @@ Restated from A2A §4 so this document stands alone: | Tier | Name | Requirements | | --- | --- | --- | -| **Tier 1** | Core | §4.5 default behavior · §6 conversation/session handling · §7 polling · §8.1–8.4 commands (`discover`, `send`, `get`, `cancel`) · §9 output & exit codes · §10.1 auth · §11 transport & versioning · §12 SKILL.md | -| **Tier 2** | Standard | Tier 1 + `list`, `subscribe`, OAuth `auth login`, ≥2 transports, config profiles, interactive `chat`, `download`, wire debug, `conformance`, shell completions | -| **Tier 3** | Advanced | Tier 2 + push-notification config CRUD and a webhook receiver, gRPC transport, authenticated extended Agent Card, Agent Card signature verification, mTLS, OpenID Connect, `serve`/mock mode, catalog/registry, extensions | +| **Tier 1** | Core | §4.5 default behavior · §6 interaction/session handling · §7 polling · §8.1–8.4 commands (`agent-inspect`, `send`, `task get`, `task cancel`) · §9 output & exit codes · §10.1 auth · §11 transport & versioning · §12 SKILL.md | +| **Tier 2** | Standard | Tier 1 + `task list`, `subscribe`, OAuth `auth login`, ≥2 transports, configuration scoping, push-notification config CRUD, `download`, wire debug, `conformance`, shell completions | +| **Tier 3** | Advanced | Tier 2 + a push-notification webhook receiver, interactive `chat`, gRPC transport, authenticated extended Agent Card, Agent Card signature verification, mTLS, OpenID Connect, `serve`/mock mode, catalog/registry, extensions | + +3.2 Conformance MUST be demonstrated by a **compliance report** (§13): the tool exercised against a live A2A agent, with an outcome recorded for every requirement identifier in the tier claimed. A tool MUST NOT advertise a tier it has not demonstrated. -3.2 Conformance MUST be demonstrated by a **compliance report** (§13) generated against the A2A Technology Compatibility Kit (TCK) and this specification. A tool MUST NOT advertise a tier it has not demonstrated. +The A2A Technology Compatibility Kit validates *agents*, not clients, so it cannot grade an `a2a-cli`. Its role here is upstream: a reporter SHOULD state that the agent they tested against is itself TCK-conformant, so that a failure can be attributed to the CLI rather than to the agent. Reporting against a non-conformant agent measures two unknowns at once. ### 3.3 Requirement identifiers @@ -95,20 +95,20 @@ Each testable requirement carries a stable identifier of the form: A2ACLI__ ``` -where `` names the command or cross-cutting concern and `` is a zero-padded sequence number within that area — for example `A2ACLI_SEND_002`, `A2ACLI_CONV_001`, `A2ACLI_OUT_003`. +where `` names the command or cross-cutting concern and `` is a zero-padded sequence number within that area — for example `A2ACLI_SEND_002`, `A2ACLI_INTERACT_001`, `A2ACLI_OUT_003`. Defined areas: | Area | Covers | Area | Covers | | --- | --- | --- | --- | | `DEFAULT` | Default behavior (§4.5) | `OUT` | Output & error contract (§9.1–9.4) | -| `DISCOVER` | Agent Card discovery (§8.1) | `EXIT` | Exit codes (§9.6) | +| `INSPECT` | Agent Card inspection (§8.1) | `EXIT` | Exit codes (§9.6) | | `SEND` | Sending messages (§8.2) | `AUTH` | Authentication (§10) | -| `GET` | Task retrieval (§8.3) | `TX` | Transport selection (§11.1) | -| `CANCEL` | Task cancellation (§8.4) | `VER` | Protocol versioning (§11.2, §11.3) | -| `LIST` | Task listing (§8.5) | `SKILL` | Agent skill descriptor (§12) | +| `TASK_GET` | Task retrieval (§8.3) | `TX` | Transport selection (§11.1) | +| `TASK_CANCEL` | Task cancellation (§8.4) | `VER` | Protocol versioning (§11.2, §11.3) | +| `TASK_LIST` | Task listing (§8.5) | `SKILL` | Agent skill descriptor (§12) | | `SUB` | Subscription / streaming (§7.4, §8.5) | `CHAT` | Interactive session (§8.5) | -| `CONV` | Conversation & session state (§6) | `CONFIG` | Profiles / environments (§8.5) | +| `INTERACT` | Interaction state (§6) | `CONFIG` | Configuration (§6.4, §8.5) | | `POLL` | Task status polling (§7) | `DOWNLOAD` | Artifact retrieval (§8.5) | | `PUSH` | Push notifications (Tier 3) | `CONFORM` | TCK conformance check (§8.5) | | `SERVE` | Local agent mode (Tier 3) | | | @@ -117,10 +117,11 @@ Defined areas: Stability rules — these make the identifiers safe to cite in tooling, test suites, and compliance reports: -- An identifier, once published, is **permanent**. It MUST NOT be renumbered, reused, or reassigned to a different requirement. +- An identifier MUST NOT be **reused**: a retired number never returns meaning something else. This holds at every status. +- **Renumbering** is permitted while this specification is a **Draft**, and is frozen from the first **Proposed** version onward. - **Tier membership is not encoded in the identifier.** A requirement may move between tiers across specification versions while keeping its identifier. - New requirements take the next unused number in their area. Numbers need not be contiguous. -- A withdrawn requirement MUST be marked `Withdrawn` in the registry rather than deleted, and its number MUST NOT be reused. +- From **Proposed** onward, a withdrawn requirement MUST be marked `Withdrawn` in the registry rather than deleted, and its number MUST NOT be reused. - New areas MAY be added; existing area names MUST NOT be repurposed. The authoritative list of requirement identifiers is the compliance-report template published alongside this specification. @@ -129,11 +130,11 @@ The authoritative list of requirement identifiers is the compliance-report templ ## 4. Design principles -4.1 **Agent-first core, dual-mode.** The default behavioral contract MUST be safe for non-interactive and programmatic use (structured output, no interactive prompts required, deterministic exit codes). A rich interactive experience for humans MAY be layered on top and MUST be gated by terminal (TTY) detection. A conformant tool is a single dual-mode tool, not two separate tools. +4.1 **Agent-first core.** The default behavioral contract MUST be safe for non-interactive and programmatic use: no interactive prompts, no terminal control sequences, deterministic exit codes, and output whose shape does not vary with the environment. This constrains **predictability, not format** — the default output is human-readable text (§4.5, §9.2); a machine consumer selects a machine format explicitly with `--output`. Any interactive mode a tool adds (for example `chat`) MUST be gated by terminal detection and MUST NOT be the default. -4.2 **Stable, versioned contract.** The machine-readable output shapes (`json` and `jsonl` — §9.3, Appendix B) and the exit-code scheme (§9.6) are a stable contract; breaking changes require a specification version bump. +4.2 **Stable, versioned contract.** The machine-readable output modes (`json` and `jsonl` — §9.3) and the exit-code scheme (§9.6) are a stable contract; breaking changes require a specification version bump. The *shapes* themselves are the A2A protocol's, not this document's (Appendix B). -4.3 **Explicit, recoverable state.** Every identifier needed to resume a conversation MUST be observable in output (§6.3). Conversation state MUST NOT exist only in process memory. +4.3 **Explicit, recoverable state.** Every identifier needed to resume an interaction MUST be observable in output (§6.3). Interaction state MUST NOT exist only in process memory. 4.4 **Transport- and language-agnostic.** Observable behavior MUST be identical across the JSON-RPC, HTTP+JSON, and gRPC bindings and across implementation languages. @@ -141,11 +142,11 @@ The authoritative list of requirement identifiers is the compliance-report templ | Behavior | Default | Override | | --- | --- | --- | -| Transport | **HTTP+JSON**, when the Agent Card offers a choice or expresses no preference (subject to card-driven selection, §11.1) | `--transport ` | +| Transport | The **first interface in the Agent Card's `supported_interfaces`** — the list is always in server preference order (§11.1) | `--transport `, repeatable, highest preference first | | Task completion | **Wait** (block) until the task reaches a terminal or interrupted state | `--async` / `--return-immediately` / `--no-wait` (return identifiers immediately) | -| Output presentation | **Human-readable, minimal yet consistently structured** text (labeled fields; not raw JSON, not a verbose dashboard) | `--output `, `-n` (machine-readable `json`) | +| Output presentation | **Human-readable `text`** — labeled fields, one field per line, no control sequences (§9.2) | `--output ` | | Detail level | **Concise** | `-v, --verbose` for detailed output | -| Protocol version | The **latest** A2A version the tool supports, signaled explicitly (§11.2) | `--a2a-version ` | +| Protocol version | The **highest version supported by both** tool and agent, signaled explicitly, never below 1.0 (§11.2) | `--a2a-version ` | | Transport security | **TLS verification enabled** | `--insecure` (development only; MUST warn) | --- @@ -156,45 +157,45 @@ The authoritative list of requirement identifiers is the compliance-report templ | Command | Tier | Purpose | | --- | --- | --- | -| `discover` | 1 | Fetch and inspect an Agent Card | -| `send` | 1 | Send a message to start or continue a conversation | -| `get` | 1 | Retrieve a task's status and artifacts | -| `cancel` | 1 | Cancel an active task | -| `list` | 2 | List tasks | -| `subscribe` | 2 | (Re)subscribe to a task's event stream | -| `chat` | 2 | Interactive multi-turn session | +| `agent-inspect` | 1 | Fetch and inspect an Agent Card | +| `send` | 1 | Send a message to start or continue an interaction | +| `task cancel` | 1 | Cancel an active task | +| `task get` | 1 | Retrieve a task's status and artifacts | | `auth` | 2 | Interactive credential acquisition (OAuth) | -| `config` | 2 | Manage named environments/profiles | -| `download` | 2 | Save task artifacts | +| `config` | 2 | Inspect and edit configuration (§6.4) | | `conformance` | 2 | Smoke-check a live agent against the A2A TCK | -| `push-config` | 3 | Manage push-notification configurations | +| `download` | 2 | Save task artifacts | +| `push-config` | 2 | Manage push-notification configurations | +| `subscribe` | 2 | (Re)subscribe to a task's event stream | +| `task list` | 2 | List tasks | +| `chat` | 3 | Interactive multi-turn session | | `serve` | 3 | Run a local mock agent (out of client baseline) | 5.2 Global options (Tier 1 MUST unless noted): | Option | Meaning | | --- | --- | -| `-u, --service-url ` | Target agent base URL (or `--env`). | -| `--context-id ` | Continue an existing conversation (§6.2). | +| `-a, --agent-card ` | The agent to talk to, given as an Agent Card reference: a host (the well-known path is appended), an explicit card URL (used as-is), or a `file://` path to a local card. | +| `--context-id ` | Continue an existing interaction (§6.2). | | `--task-id ` | Continue an existing task (§6.2). | -| `--continue` / `--last` | Resume the stored last conversation (§6.4). | -| `-o, --output ` | Output mode. Default: minimal, structured `text` (§4.5). `tui` is an opt-in interactive mode; `json` emits one complete document; `jsonl` emits one JSON object per line for streaming (§9.3). | -| `-n` | Alias for `--output json`, non-interactive. | -| `--transport ` | Override the transport binding (default HTTP+JSON, §4.5), subject to the Agent Card (§11.1). | +| `-o, --output ` | Output mode. Default `text` (§4.5, §9.2). `json` emits exactly one document; `jsonl` emits one object per line (§9.3). A tool MUST NOT change the selected mode implicitly. | +| `--transport ` | Client transport preference, **repeatable and ordered** (highest first). Overrides the card's preference order (§11.1); a binding absent from the card is skipped. | | `--async` / `--return-immediately` / `--no-wait` | Do not wait; return the task identifiers immediately for later polling (default is to wait, §4.5 / §7.3). | | `--wait` / `--watch` | Block until the task reaches a terminal or interrupted state. This is the default for `send` (§4.5); stating it explicitly overrides a configured default. On `get` it turns the one-shot read into a poll loop (§7.3). | | `--poll-interval ` / `--timeout ` | Polling controls (§7.3). | | `--bearer ` / `--api-key ` / `-H, --header ` | Credentials (§10.1). | | `--a2a-version ` | Protocol version to signal (§11). | | `--env ` | Named profile (Tier 2). | -| `-v, --verbose` | Detailed output; additional diagnostics to stderr. `--dump-wire` (Tier 2) emits raw protocol JSON. | -| `--insecure` | Disable TLS verification (development only; MUST emit a warning). | +| `-v, --verbose` | **Presentation:** show the full part structure rather than collapsing parts into one representation. | +| `--debug` | **Diagnostics:** verbose logging to stderr. `--dump-wire` (Tier 2) additionally emits raw protocol JSON. | +| `--insecure` | Disable TLS verification for the negotiated transport (development only; MUST emit a warning). Transport security is on unless this is passed. | +| `--metadata ` | Request metadata, for protocol extensions (A2A §3.2.5). | --- -## 6. Conversation & session state +## 6. Interaction state & configuration -A2A conversations span multiple invocations; the CLI is the bridge that carries state between them. +A2A interactions MAY span multiple invocations. **The CLI itself is stateless**: it reports every identifier it receives and accepts every identifier as input, but it does not remember one invocation in the next. What persists is *configuration* (§6.4), not session state. ### 6.1 Identifiers @@ -202,21 +203,22 @@ A2A conversations span multiple invocations; the CLI is the bridge that carries | --- | --- | --- | --- | | `messageId` | Client | Turn identity / idempotency | SHOULD be reused when retrying a turn, because Send is not guaranteed idempotent; reuse avoids duplicated work. | | `taskId` | Server | Unit of work | A tool MUST NOT invent a `taskId` for a new task. A client-supplied `taskId` MUST reference an existing task; otherwise the server returns a not-found error. | -| `contextId` | Server | Conversation grouping | Opaque; a tool SHOULD NOT fabricate one. A `contextId` and `taskId` that do not correspond MUST be rejected by the server; a tool MUST NOT attempt to reconcile them. | +| `contextId` | Server | Context grouping | Opaque; a tool SHOULD NOT fabricate one. A `contextId` and `taskId` that do not correspond MUST be rejected by the server; a tool MUST NOT attempt to reconcile them. | -A conformant tool never *creates* server identifiers; it **captures** them from responses and **replays** them on later turns. +A conformant tool never *creates* server identifiers. It reports the ones the server assigned (§6.3) and accepts them back as explicit input (§6.2); it MUST NOT store them and replay them on the caller's behalf. -### 6.2 Continuing a conversation (MUST) +### 6.2 Continuing an interaction (MUST) A conformant tool MUST allow continuation via explicit options: -- **`--context-id `** attaches this turn to an existing conversation (a new task within the same context). +- **`--context-id `** attaches this turn to an existing context (a new task within that context). - **`--task-id `** continues an existing task — for example, to respond to a task waiting in `INPUT_REQUIRED` (§7.1). Rules: -- When `--task-id` is supplied, the tool MUST send the message against that task and MUST surface any server error (e.g. not-found or state conflict) rather than silently starting a new task. +- `--task-id` MUST be accompanied by `--context-id`, so the pair can never be mismatched. +- When `--task-id` is supplied, the tool MUST send the message against that task. If the server rejects the identifier — not found, or a terminal-state conflict (A2A §3.1.1) — the tool MUST emit a **warning** naming both the requested and the actual task identifier, then continue in the same context, and MUST carry both identifiers in machine-readable output so the substitution is detectable without reading stderr. It MUST point the caller at `--debug` for the underlying protocol error, and MUST NOT abort: a stale identifier is a routine retry, not a broken invocation. - When only `--context-id` is supplied, the tool sends a message under that context which MAY return a message or Task -- When both are supplied, the tool MUST pass them through unchanged. +- Both supplied is the normal case for task continuation; the tool MUST pass them through unchanged. - Interactive `chat` (Tier 2) MUST carry the `contextId` — and the active `taskId` while a task is interrupted — across turns automatically. ### 6.3 Reporting identifiers back (MUST) @@ -224,15 +226,24 @@ Rules: Because the next invocation depends on them, every command that touches a task MUST expose, on completion and on interruption: - the **`taskId`**, the **`contextId`**, and the current **task state**; -- in `--output json`, these MUST appear as stable top-level fields (`taskId`, `contextId`, `state`) per Appendix B; +- in `--output json` and `--output jsonl`, these are carried by the protocol response type itself (Appendix B) — a tool MUST NOT flatten or rename them into fields of its own; - in human-facing modes, these MUST be printed in a copy-pasteable form, and the tool SHOULD print the exact command required to resume (for example, `a2a-cli send --task-id ""`). -### 6.4 Local session state (SHOULD) +### 6.4 Configuration (SHOULD) -- A tool SHOULD persist the most recent conversation (`contextId`, latest `taskId`, service URL) so a caller can resume without re-supplying identifiers (e.g. `--continue`). -- A tool SHOULD support named profiles/environments (service URL, credentials, defaults) selected with `--env`. -- Persisted data MUST reside under a conventional configuration path, MUST NOT store secrets in world-readable files (secret files MUST be mode `0600` or the platform equivalent), and MUST be inspectable and clearable by the user. -- Explicit options MUST override stored state. +A tool persists **configuration**, never session state. It MUST NOT record the last `contextId` or `taskId` and offer to resume from them: a caller that wants to continue supplies the identifier (§6.2), which keeps the tool stateless and the contract obvious. + +Configuration values resolve in one fixed order, highest wins: + +1. an explicit flag, +2. an environment variable, +3. a local configuration file, +4. a global configuration file, +5. the built-in default (§4.5). + +Files SHOULD be discovered the way `git` discovers its configuration: a global file, then a local file found by walking up from the working directory. Settings SHOULD be scopeable **by agent-card reference**, so a caller supplies the reference and inherits the right credentials and defaults for that agent without naming a profile. + +Persisted data MUST reside under a conventional configuration path, MUST NOT store secrets in world-readable files (secret files MUST be mode `0600` or the platform equivalent), and MUST be inspectable and clearable by the user. --- @@ -260,36 +271,39 @@ A conformant tool MUST provide a polling path: - **`get `** — one-shot retrieval of task state (with artifacts via `--include-artifacts`, history via `--history `). - **A blocking/watch mode** that repeatedly polls until a **terminal** state and **stops immediately on an interrupted** state, returning so the caller can act. This is the default behavior of `send` (§4.5) and is available on `get` via `--wait` / `--watch`. - Polling controls: `--poll-interval` (RECOMMENDED default 2 seconds) and `--timeout` (on expiry the tool MUST exit non-zero with the timeout code, §9). A tool SHOULD apply bounded backoff, MUST NOT busy-loop, and MUST remain interruptible without losing the already-printed `taskId`. -- When both streaming and polling are available, a blocking wait MAY prefer streaming and MUST fall back to polling on stream failure. Because messages are not a reliable delivery mechanism, after any stream reconnect the tool MUST reconcile final state with a `get`. +- When both streaming and polling are available, a blocking wait MAY prefer streaming and MUST fall back to polling on stream failure. *(Reconciling after a reconnect needs no separate rule: A2A streaming already re-sends the full `Task` as the first event on reconnect.)* ### 7.4 Stream resumption (SHOULD) -For long-running tasks, a tool SHOULD support reconnection via `subscribe` (whose first event is the `Task`, closing the gap between a poll and a subscribe) and, where the server supports it, resumption from the last received event. After any reconnect, the tool MUST reconcile with a `get`. +For long-running tasks, a tool SHOULD support reconnection via `subscribe` (whose first event is the `Task`, closing the gap between a poll and a subscribe) and, where the server supports it, resumption from the last received event. Because that first event carries the full `Task`, state is reconciled by the protocol itself and no additional `get` is required. --- ## 8. Command specifications -### 8.1 `discover` (Tier 1, MUST) -Fetch the Agent Card from the well-known location (`/.well-known/agent-card.json`) or an explicit `--card-url`, then parse and present: identity, advertised capabilities (streaming, push notifications, extended card), declared interfaces/transports, security schemes, and skills. The tool MUST use the card to select a transport (§11). It SHOULD offer `--validate` to check the card against the A2A schema and SHOULD cache the card honoring HTTP caching semantics. +### 8.1 `agent-inspect` (Tier 1, MUST) +Resolve the Agent Card from `--agent-card` — a host (the well-known path `/.well-known/agent-card.json` is appended), an explicit card URL, or a local `file://` path — then parse and present: identity, advertised capabilities (streaming, push notifications, extended card), declared interfaces/transports, security schemes, and skills. The tool MUST use the card to select a transport (§11). It SHOULD offer `--validate` to check the card against the A2A schema, SHOULD offer `--extended` to fetch the authenticated extended card (Tier 3, §10.4), and SHOULD cache the card honoring HTTP caching semantics. + +The command is named `agent-inspect` rather than `discover` because "discovery" already names the *process* of resolving a card from a reference; using it for the command invites confusion with that broader sense. ### 8.2 `send` (Tier 1, MUST) -Send a message to **start or continue** a conversation. +Send a message to **start or continue** an interaction. - Blocking by default (the operation waits until the task reaches a terminal or interrupted state, §4.5); `--async` / `--return-immediately` returns the `taskId` immediately instead. -- Accepts `--context-id` / `--task-id` (§6.2); message parts via `--text`, `--file`, `--data`; `--stream` (§7.2); polling controls (§7.3). -- With `--stream`, the tool consumes the event stream when the streaming capability is present; the first event MUST be the `Task`. If streaming is unsupported the tool MUST fall back or error clearly and MUST NOT hang. +- Accepts `--context-id` / `--task-id` (§6.2); `--stream` (§7.2); polling controls (§7.3); `--metadata` for extension data. +- **Message parts.** A message MAY carry more than one part, and the part flags are repeatable and order-preserving: `--text `, `--file ` (a local filesystem path), `--data ` (a JSON file, or `-` to read from stdin). A tool MUST let the caller set the media type of a part explicitly — file extensions are not reliable indicators and are frequently absent — and SHOULD infer it only when not given. +- With `--stream`, the tool consumes the event stream when the streaming capability is present. If the agent returns a `Message` rather than a `Task` the stream contains exactly that one object and closes; the tool MUST exit cleanly with the output rather than treating it as an error. If streaming is unsupported the tool MUST fall back or error clearly, and MUST NOT hang. - On `INPUT_REQUIRED` or `AUTH_REQUIRED`, the tool MUST stop and report the `taskId`, `contextId`, and state with a resume hint (§6.3), and MUST NOT deadlock. -- The tool MUST render produced artifacts. +- **The tool MUST render produced artifacts**, meaning: a text part is printed as readable text rather than dumped as raw structure; a data part is printed as formatted JSON; a file part has its name, media type and size reported, and its content written to disk only when the caller asked for it (`download`, Tier 2). Rendering never silently discards a part. -### 8.3 `get` (Tier 1, MUST) +### 8.3 `task get` (Tier 1, MUST) Retrieve a task by identifier: state, artifacts (`--include-artifacts`), and optionally history (`--history `). One-shot by default; `--wait` / `--watch` polls until a terminal or interrupted state (§7.3). MUST report `taskId`, `contextId`, and state. -### 8.4 `cancel` (Tier 1, MUST) +### 8.4 `task cancel` (Tier 1, MUST) Cancel an active task by identifier. The operation is idempotent and MAY return a not-cancelable error if the task has already reached a terminal state. MUST report the resulting state. ### 8.5 Higher-tier commands (outline) -- **Tier 2:** `list` (cursor-paginated, filterable by status and context); `subscribe` (stream reconnect); `auth login` (OAuth 2.1 device-code and client-credentials flows with a secure token store); multi-transport selection; `config` (named profiles); `chat` (interactive multi-turn); `download` (save artifacts); `--dump-wire`; `conformance` (TCK smoke check); shell completions. -- **Tier 3:** `push-config` create/get/list/delete plus a webhook receiver; gRPC transport; authenticated extended Agent Card; Agent Card signature verification; mTLS; OpenID Connect; `serve`/mock agent; catalog/registry integration; batch/stdin input; protocol extensions. +- **Tier 2:** `task list` (cursor-paginated, filterable by status and context); `subscribe` (stream reconnect); `auth login` (OAuth 2.1 device-code and client-credentials flows with a secure token store); multi-transport selection; `config` (inspect and edit configuration, §6.4); `push-config` create/get/list/delete; `download` (save artifacts); `--dump-wire`; `conformance` (TCK smoke check); shell completions. +- **Tier 3:** a webhook receiver for push notifications; `chat` (interactive multi-turn); gRPC transport; authenticated extended Agent Card; Agent Card signature verification; mTLS; OpenID Connect; `serve`/mock agent; catalog/registry integration; batch/stdin input; protocol extensions. --- @@ -297,7 +311,9 @@ Cancel an active task by identifier. The operation is idempotent and MAY return 9.1 In a machine-readable mode (`json` or `jsonl`), a tool MUST emit only the structured payload on **stdout**; all diagnostics, prompts, progress indicators, and logs MUST go to **stderr**. The two streams MUST NOT be mixed. -9.2 When `tui` is in effect (selected explicitly or by configuration), a tool MUST auto-degrade to `text` if stdout is not a terminal, producing no terminal control sequences and never blocking on interactive input. The default output mode is already non-interactive `text` (§4.5). +9.2 **The `text` mode floor.** `text` is the default (§4.5) and MUST be safe to parse and to pipe. A tool MUST emit one `Label: value` field per line, MUST use the same labels across invocations, MUST NOT emit terminal control sequences, and MUST include the task identifier, the context identifier, and the task state for any command that touches a task. + +Any interactive mode a tool offers beyond this (for example `chat`, Tier 3) MUST auto-degrade to `text` when stdout is not a terminal, and MUST never block on interactive input in that case. ### 9.3 Machine-readable modes: `json` and `jsonl` @@ -305,25 +321,31 @@ A conformant tool MUST support **both** machine-readable modes. They serve diffe | Mode | Shape | Use it when | | --- | --- | --- | -| **`json`** | Exactly **one** complete JSON document written once, when the result is known | The agent responds quickly, or the caller wants the whole result in a single parse — the common scripting case | +| **`json`** | Exactly **one** complete JSON document — the terminal protocol object — written once, when the result is known | The caller wants the outcome in a single parse — the common scripting case | | **`jsonl`** | **One JSON object per line** ([JSON Lines](https://jsonlines.org/)), flushed as each event occurs | The caller consumes progress incrementally — streaming agents, and agentic apps/harnesses that render or act on partial output | -- **`json` MUST buffer**: even when the underlying interaction streams, the tool MUST emit a single final document, never a concatenation of objects. A `json` consumer can always `JSON.parse` stdout in one shot. +- **`json` MUST be a single document**: even when the underlying interaction streams, the tool MUST emit exactly one object — the **terminal** protocol object (the final `Task`, or the `Message` where no task was created), never a concatenation of events. A `json` consumer can always `JSON.parse` stdout in one shot, and the size is bounded by the task rather than by how many events it produced. - **`jsonl` MUST stream**: each line MUST be a complete, independently parseable JSON object terminated by a newline, flushed as it is produced so a reader can consume the stream incrementally. Lines MUST NOT be pretty-printed across multiple physical lines. - If a tool cannot stream a given interaction (streaming unsupported by the agent, or a one-shot command such as `cancel`), `jsonl` MUST still be honored by emitting the applicable object(s), one per line — a single-line result is valid JSONL. -- Both modes MUST use the envelope in Appendix B and MUST include the stable fields defined in §6.3. +- Both modes MUST emit the A2A protocol's own response types (Appendix B). A tool MUST NOT define a substitute schema, and MUST NOT change the selected mode implicitly — a caller that asked for `json` and received `jsonl` will parse the first line and treat it as the whole result, failing silently with a plausible answer. 9.4 Errors MUST be machine-readable in both modes (the error envelope in Appendix B) and MUST be normalized across transports so that the same A2A error yields the same tool-level result regardless of binding. In `jsonl`, an error terminating the stream MUST be emitted as a final error object on its own line. -The `code` field MUST carry a stable, symbolic error identifier of the form `A2ACLI_ERR_`, drawn from the registry in Appendix E. Symbolic codes — rather than bare numbers — let a caller match on meaning, keep working as the registry grows, and read clearly in logs. +Errors come in two layers, and a tool MUST NOT blur them. + +**Protocol failures** carry the A2A error the server reported, by name, unchanged — `TaskNotFoundError`, `UnsupportedOperationError`, `ContentTypeNotSupportedError` and the rest of the set defined in **A2A §3.3.2**, mapped across bindings per **A2A §5.4**. This specification does not restate that set and does not rename it. (§11.6 of A2A defines the same handling for the HTTP+JSON binding only; citing it here would tie the CLI's errors to one transport, contradicting §4.4.) + +**CLI-local failures** are the conditions the protocol has no opinion on, because they happen before or outside any request: a malformed flag, an unresolvable `--agent-card` reference, an unreadable local card, no network. Those carry a symbolic `A2ACLI_ERR_` identifier from Appendix E. -Only the **eight core codes** in Appendix E.1 are required. The extended codes in E.2 are optional refinements for tools that can tell those cases apart; a tool that cannot classify a condition MUST fall back to the applicable core code (`A2ACLI_ERR_INTERNAL` if none is closer). Tools MUST NOT invent codes in the `A2ACLI_ERR_` namespace; vendor-specific codes MUST use a distinct prefix. +A condition the protocol already names MUST carry the protocol's error rather than an `A2ACLI_ERR_` code. A tool that cannot classify a CLI-local condition MUST report `A2ACLI_ERR_INTERNAL`. Tools MUST NOT invent codes in the `A2ACLI_ERR_` namespace; vendor-specific codes MUST use a distinct prefix. A tool SHOULD also populate the envelope's `hint` field with an actionable next step (Appendix B). A precise code tells a program what happened; a good hint tells a person what to do about it, and costs far less to implement than the rest of this section. 9.5 When the caller does not wait for completion (`--async` / `--return-immediately` / `--no-wait`), the tool MUST still emit a result object carrying the identifiers required to resume or poll later — at minimum `taskId` and `contextId` (§6.3) — so the caller can query status with `get` at a later time. -9.6 Exit codes. Every error code in Appendix E maps to exactly one of these: the exit code is the coarse signal for shells and CI, the error code the precise one for programmatic callers. +9.6 Exit codes. The exit code is the coarse signal for shells and CI; the error code is the precise one for programmatic callers. Every error — whether it carries an A2A error name or an `A2ACLI_ERR_*` identifier — MUST map to exactly one of these. + +`0`, `1` and `2` come free with any modern argument parser, and `3` follows from a failed connection. Codes `4` through `7` require the tool to classify the failure, and each exists so a caller can branch on it — retry on a timeout, prompt on input-required, fail a build on a failed task. | Code | Meaning | | --- | --- | @@ -340,9 +362,11 @@ A tool SHOULD also populate the envelope's `hint` field with an actionable next ## 10. Authentication -10.1 **Tier 1 (MUST):** scriptable, caller-supplied credentials — `--bearer`, `--api-key`, and `-H/--header`, with environment-variable equivalents. Credentials are attached per request in transport headers or metadata (A2A conveys identity at the transport layer, not in the payload). +10.1 **Tier 1 (MUST):** scriptable, caller-supplied credentials — `--bearer` and `--api-key`, with environment-variable equivalents. Credentials are attached per request as **service parameters** (A2A §3.2.6), which each binding maps to its own mechanism — an HTTP header, a query parameter, or gRPC metadata. A2A conveys identity at the transport layer, not in the payload. + +`-H/--header` is a **separate, general-purpose** option for attaching any additional service parameter, not only credentials, and MUST NOT be documented as an authentication flag. -10.2 **Tier 2 (SHOULD):** interactive OAuth 2.1 via `auth login`, supporting the device-code flow (designed for CLIs) and the client-credentials flow, with secure token storage and automatic attachment on subsequent calls. +10.2 **Tier 2 (SHOULD):** interactive OAuth 2.1 via `auth login`, supporting the device-code flow ([RFC 8628](https://www.rfc-editor.org/rfc/rfc8628), designed for input-constrained clients such as a CLI) and the client-credentials flow, with secure token storage and automatic attachment on subsequent calls. 10.3 **Tier 3 (MAY):** mutual TLS and OpenID Connect. A tool at this tier SHOULD also handle the in-task `AUTH_REQUIRED` state, a second authentication path that can occur mid-task. @@ -352,27 +376,43 @@ A tool SHOULD also populate the envelope's `hint` field with an actionable next ## 11. Transport & version negotiation -11.1 **Transport selection (MUST):** a tool MUST select a binding from the Agent Card's declared interfaces (honoring the declared preference order) and MUST NOT assume a single transport. When the card offers multiple bindings without a decisive preference, the tool defaults to **HTTP+JSON** (§4.5), overridable with `--transport`. When an interface declares a routing identifier, the tool MUST echo it on every request. +11.1 **Transport selection (MUST):** a tool MUST select a binding from the Agent Card's declared interfaces and MUST NOT assume a single transport. `supported_interfaces` is **always in server preference order**, so absent any client preference a tool MUST use the first entry it supports. + +A client MAY express its own preference with `--transport`, which is **repeatable and ordered**: the tool takes the first client-preferred binding the card also offers, and falls back to the card's order when none matches. A single-valued preference is insufficient — it leaves a tool with no way to negotiate against a card that does not offer it. When an interface declares a routing identifier, the tool MUST echo it on every request. -11.2 **Protocol version (MUST):** a tool MUST signal the A2A protocol version on every request. This is a per-binding service parameter conveyed as an HTTP header, a query parameter, or gRPC metadata depending on the transport; an empty value causes the server to assume a legacy version, so the tool MUST set it explicitly. The tool SHOULD expose `--a2a-version` and MUST surface a version-unsupported error clearly rather than silently downgrading. +11.2 **Protocol version (MUST):** a tool MUST signal the A2A protocol version on every request. This is a per-binding service parameter conveyed as an HTTP header, a query parameter, or gRPC metadata depending on the transport; an empty value causes the server to assume a legacy version, so the tool MUST set it explicitly. The tool SHOULD expose `--a2a-version`. Absent an explicit value, a tool SHOULD negotiate down to the highest version supported by both itself and the agent as declared on the Agent Card, but MUST NOT negotiate below **1.0** — versions earlier than 1.0 are legacy and MUST require an explicit opt-in. A tool MUST surface a version-unsupported error clearly rather than silently downgrading. -11.3 **Capability validation (SHOULD):** before invoking a capability-gated operation (streaming, push notifications, extended card), a tool SHOULD verify the capability on the Agent Card, and MUST declare any server-required extensions it supports. +11.3 **Capability validation (SHOULD):** before invoking a capability-gated operation (streaming, push notifications, extended card), a tool SHOULD verify the capability on the Agent Card. + +Declaring the server-required extensions a tool supports is a separate, Tier 3 requirement (`A2ACLI_VER_002`) and is not required of a Tier 1 tool. --- -## 12. Agent integration (SKILL.md) +## 12. Agent integration (Agent Skills) -12.1 A conformant tool MUST ship a machine-readable agent skill descriptor (`SKILL.md`) that instructs an AI coding agent how to drive the tool: use `--output json` for a single parseable result, or `--output jsonl` to consume progress incrementally (§9.3); rely on blocking completion (the default, or `--wait` on `get`) rather than ad-hoc sleeps; determine success from the reported task state; **capture and replay `taskId` and `contextId` to sustain a multi-turn conversation**; and use scriptable credentials rather than interactive login. +> **Terminology.** "Agent Skill" in this section means a directory containing a `SKILL.md`, per the Agent Skills format [AGENT-SKILLS]. It is unrelated to the `AgentSkill` object A2A defines at §4.4.5, which describes a capability advertised on an Agent Card. The two are different things that unfortunately share a name. -12.2 **One lightweight, generic skill.** A conformant tool MUST ship exactly one skill descriptor — not one per tier, per command, or per capability. It MUST be generic and token-efficient: it MUST NOT enumerate the full command surface or embed all capabilities inline, and instead MUST direct the agent to discover capabilities at runtime (for example, beginning with `a2a-cli help` / `a2a-cli --help`), keeping the always-loaded context footprint minimal. +12.1 **Conditional, and exactly one.** A tool is not required to ship an Agent Skill. **If it ships one, it MUST ship exactly one** — not one per tier, per command, or per capability. A specification that standardises the command surface makes a single skill sufficient for any conformant tool, so shipping several is duplication rather than coverage. -12.3 **Distinct layers; bundled and self-installable.** The specification (the behavioral contract) and the skill (agent-facing usage guidance) are DISTINCT layers and MUST be maintained separately; the skill MUST NOT restate normative requirements. The tool and its skill SHOULD be distributed as a single bundle that an AI coding agent can discover and install itself, following an established agent-plugin convention. +The skill instructs an AI coding agent how to drive the tool: use `--output json` for a single parseable result, or `--output jsonl` to consume progress incrementally (§9.3); rely on blocking completion rather than ad-hoc sleeps; determine success from the reported task state; pass `--context-id` and `--task-id` explicitly, since the tool holds no session state (§6); and use scriptable credentials rather than interactive login. ---- +12.2 **Lean, deferring to runtime help.** The `SKILL.md` itself MUST be generic and token-efficient: it MUST NOT enumerate the full command surface or embed every capability inline, and MUST direct the agent to discover capabilities at runtime (for example `a2a-cli help`, `a2a-cli --help`), keeping the always-loaded context footprint small. + +Worked examples — a command with representative input and output — are genuinely useful and SHOULD live in a references file alongside `SKILL.md` rather than in the body, so they cost nothing until an agent needs them. + +12.3 **Distinct layers.** The specification (the behavioural contract) and the skill (agent-facing usage guidance) are DISTINCT layers and MUST be maintained separately. The skill MUST NOT restate normative requirements: an agent needs to know how to invoke the tool, not which clause obliges it. + +12.4 **Distribution.** A skill SHOULD be installable into the cross-client location `/.agents/skills/a2a-cli/`. *(The Agent Skills format does not define an installation location; this is a widely-adopted convention, not a normative requirement of that format.)* + +A skill **MUST NOT** assume it can install the `a2a-cli` binary — no current agent-facing standard defines an installation mechanism. It SHOULD declare the dependency as human-readable prose in the `compatibility` frontmatter field, and SHOULD include a preflight check and install pointers in its body. Binary distribution is out of scope here and belongs to platform package managers. + +A tool shipping **more than one** agent-facing component — a skill together with an MCP server, say — MAY additionally distribute them as an Agent Plugin package [AGENT-PLUGINS]. A tool shipping a single skill SHOULD NOT wrap it in a plugin. Agent Plugins defines packaging only; installation, distribution and permissions remain client-controlled. ## 13. Compliance report & compatibility matrix -13.1 A tool asserting conformance MUST publish a **compliance report** stating: the tier claimed; per-command pass/fail; the A2A TCK version(s) exercised; transports covered; and conformance to the conversation/session (§6) and polling (§7) requirements. +13.1 A tool asserting conformance MUST publish a **compliance report** stating: the tier claimed; the outcome **per requirement identifier** (§3.3), not merely per command; the agent it was exercised against, and whether that agent is TCK-conformant (§3.2); transports covered; and a note on every result that is not a plain pass. + +A requirement the reporter could not provoke — an agent that never returns `INPUT_REQUIRED`, say — MUST be recorded as such with the reason, never assumed to pass. An unobservable requirement and a satisfied one are different results. 13.2 The A2A project publishes a **compatibility matrix** (tools × features/tiers) so users can compare implementations. Entries are backed by compliance reports rather than being self-asserted alone. This evidence-based matrix — not self-declaration — is what substantiates a tool's advertised tier. @@ -400,33 +440,39 @@ This specification does not: define server/agent behavior (`serve` mode is optio | Command | A2A operation | A2A reference | Tier | | --- | --- | --- | --- | -| `discover` | Get Agent Card / Get Extended Agent Card | §8 / §3.1.11 | 1 (extended: 3) | +| `agent-inspect` | Get Agent Card / Get Extended Agent Card | §8 / §3.1.11 | 1 (extended: 3) | | `send` | Send Message / Send Streaming Message | §3.1.1 / §3.1.2 | 1 | -| `get` | Get Task | §3.1.3 | 1 | -| `cancel` | Cancel Task | §3.1.5 | 1 | -| `list` | List Tasks | §3.1.4 | 2 | +| `task cancel` | Cancel Task | §3.1.5 | 1 | +| `task get` | Get Task | §3.1.3 | 1 | +| `push-config` | Create / Get / List / Delete Push Notification Config | §3.1.7–§3.1.10 | 2 | | `subscribe` | Subscribe to Task | §3.1.6 | 2 | -| `push-config` | Create / Get / List / Delete Push Notification Config | §3.1.7–§3.1.10 | 3 | +| `task list` | List Tasks | §3.1.4 | 2 | -## Appendix B — Minimal JSON output envelope (normative) +## Appendix B — Machine-readable output (normative) -In a machine-readable mode (`json` or `jsonl`), task-affecting commands (`send`, `get`, `cancel`, and per-item `list`/`subscribe` output) MUST emit at least the following fields. Tools MAY add fields; consumers MUST ignore unknown fields. +**This specification defines no output schema of its own.** In `json` and `jsonl` modes a tool MUST emit the A2A protocol's own response types, unmodified, as defined by `spec/a2a.proto` and rendered per A2A's JSON field-naming convention (A2A §5.5). Inventing a CLI-level envelope would oblige every consumer to unwrap it, break protocol schema validators, and commit this specification to a second versioned schema with its own deprecation policy. -**Task-operation object:** -```json -{ - "taskId": "string | null", - "contextId": "string | null", - "state": "TASK_STATE_*", - "artifacts": [], - "message": null -} -``` -- `taskId`, `contextId`, `state` — REQUIRED (may be `null` only when no task was created, e.g. a direct message response). -- `artifacts` — REQUIRED when artifacts were requested/available; otherwise MAY be omitted or empty. -- `message` — the direct message response when the server returned a Message rather than a Task; otherwise `null`. +**Which type, per command:** + +| Command | `--output json` emits | `--output jsonl` emits, one per line | +| --- | --- | --- | +| `send` | `SendMessageResponse` — the terminal object: `task` when a task was created, otherwise `message` | `StreamResponse` per event | +| `task get` | `Task` | `Task` (single line) | +| `task cancel` | `Task` | `Task` (single line) | +| `task list` | `ListTasksResponse` | one `Task` per line | +| `subscribe` | the terminal `Task` | `StreamResponse` per event | +| `agent-inspect` | `AgentCard` | `AgentCard` (single line) | + +`SendMessageResponse` and `StreamResponse` are discriminated unions (protobuf `oneof`), which is what makes them scriptable: a consumer switches on which field is present rather than inspecting the shape. A tool MUST NOT add a discriminator field of its own — the `oneof` is the discriminator. + +Tools MAY add fields outside the protocol types only where the protocol provides an extension point; consumers MUST ignore unknown fields. + +**Streaming (`--output jsonl`).** One `StreamResponse` per line, flushed as produced, each a complete JSON object on a single physical line. The final line MUST carry the terminal `Task` (or `Message`), so a reader that keeps only the last line still obtains the task identifier, the context identifier, and the state. + +**Single document (`--output json`).** Exactly one object for the whole invocation — the terminal object above, or one error object. Never a concatenation, and never the event log. + +**Errors.** A failure emits one error object instead of a result: -**Error object** (mutually exclusive with a successful result): ```json { "error": { @@ -437,22 +483,23 @@ In a machine-readable mode (`json` or `jsonl`), task-affecting commands (`send`, } } ``` -- `code` — REQUIRED, a symbolic `A2ACLI_ERR_` identifier from Appendix E (stable across transports). +- `code` — REQUIRED. For a protocol failure this is the A2A error name (§9.4). For a condition the protocol has no opinion on — a malformed flag, an unresolvable `--agent-card` reference, an unreadable local card — it is an `A2ACLI_ERR_` identifier from Appendix E. - `message` — REQUIRED, human-readable. - `hint` — RECOMMENDED. A short, actionable next step, ideally a copy-pasteable command. Derive it from context where possible — for example, reading the Agent Card's security schemes to name the exact login command for that agent. Omit it, or set `null`, when there is nothing useful to say; never pad it. -- `a2aCode` — the underlying A2A/transport error code when one exists, else `null`. +- `a2aCode` — the underlying transport-level code when one exists, else `null`. -**Streaming (`--output jsonl`):** one JSON object per line, flushed as produced. Each line MUST carry a `type` field identifying the event (for example `status`, `artifact`, `result`, `error`), and the final/terminal line MUST include the task-operation fields above so a reader that keeps only the last line still obtains `taskId`, `contextId`, and `state`. +## Appendix C — References -**Single document (`--output json`):** exactly one task-operation object (or one error object) for the whole invocation. +**Normative.** These define terms this specification depends on. -## Appendix C — References +- A2A Protocol Specification v1.0 — https://a2a-protocol.org/latest/specification/ — the authority for all protocol semantics, data types, error names and binding behaviour referenced as "A2A §x". +- RFC 2119 — key words for requirement levels. +- RFC 8628 — OAuth 2.0 Device Authorization Grant (§10.2). +- [AGENT-PLUGINS] Agent Plugins Specification v1.0.0 — https://agent-plugins.org/specification (§12.4). + +**Informative.** -- A2A Protocol Specification v1.0 — https://a2a-protocol.org/latest/specification/ -- A2A Agent Discovery — https://a2a-protocol.org/latest/topics/agent-discovery/ -- A2A Streaming & Asynchronous Operations — https://a2a-protocol.org/latest/topics/streaming-and-async/ -- RFC 2119 — Key words for requirement levels -- RFC 8628 — OAuth 2.0 Device Authorization Grant +- [AGENT-SKILLS] Agent Skills specification — https://agentskills.io/specification (accessed 2026-08-11). Cited informatively: the document carries no version and publishes no governance model, so this specification does not bind conformance to it. ## Appendix D — Revision history @@ -460,48 +507,28 @@ While the specification is in Draft, notable revisions are recorded by date; the | Version | Date | Notes | | --- | --- | --- | -| 0.1 (Draft) | 2026-08-10 | Added a RECOMMENDED `hint` field to the error envelope (Appendix B, §9.4) — an actionable next step alongside the machine-readable code. Restructured "Why this matters" into three sourced problems, adding testing a running agent as a distinct motivation. Split the error registry into eight required core codes (E.1) and optional extended refinements (E.2) so conformance stays cheap. Added the requirement-identifier convention (§3.3) and a symbolic error vocabulary. | -| 0.1 (Draft) | 2026-08-06 | Defined two machine-readable output modes — `json` (exactly one buffered document) and `jsonl` (one object per line, flushed as produced) — and required both (§9.3). Renamed NDJSON to JSONL throughout. Added `--no-wait` as an alias for `--async` / `--return-immediately`, and required that not waiting still returns `taskId` and `contextId` for later polling (§9.5). | -| 0.1 (Draft) | 2026-08-04 | Made governance implementation-neutral: removed the designation of a specific reference implementation and language from §15.3, and aligned §1.4, §14 and §15.2. | -| 0.1 (Draft) | 2026-08-04 | Initial draft. Tier 1 normative; Tiers 2–3 outlined. Client-only baseline; conversation/session state (§6) and task polling (§7) as first-class; opinionated defaults (§4.5); conformant-vs-official + governance (§1.4, §15); normative output envelope (Appendix B). | +| 0.1 (Draft) | 2026-08-11 | Terminology: interaction, not conversation. Transport honours the Agent Card's preference order; `--transport` is repeatable and ordered; version negotiates down only within 1.x. Machine-readable output emits the protocol's own types — Appendix B defines no schema — with `json` as the terminal object and no implicit switch to `jsonl`; `tui` removed and §9.2 pins the `text` floor. The CLI is stateless: no capture-and-replay, no `--continue`; §6.4 keeps configuration only, with a documented precedence. Commands namespaced (`task get`, `task list`), `discover` → `agent-inspect`, `--service-url` and `--card-url` → `--agent-card`. Errors defer to the A2A set (§3.3.2, §5.4); Appendix E reduced to CLI-local conditions. `chat` → Tier 3, `push-config` → Tier 2. Shipping a skill is conditional. Conformance is demonstrated against a live agent rather than the TCK, which validates agents rather than clients. Identifier permanence binds from Proposed. | +| 0.1 (Draft) | 2026-08-04 | Initial draft. Client-only baseline; Tier 1 normative with Tiers 2–3 outlined; interaction and session handling (§6) and task polling (§7) as first-class; opinionated defaults (§4.5); conformant-versus-official and governance (§1.4, §15). | -## Appendix E — Error code registry (normative) +## Appendix E — CLI-local error codes (normative) -Values for the `code` field of the error envelope (Appendix B). +Values for the `code` field (Appendix B) **when the failure is the CLI's own**. A failure the protocol already names carries that A2A error instead (§9.4, A2A §3.3.2); this registry does not restate or rename the protocol's error set. -The registry is split so that conformance stays cheap: a tool needs only the **core** codes below. The **extended** codes exist for tools that can tell failures apart more precisely — using them is encouraged but never required, and a tool that cannot distinguish a case simply reports the core code instead. +The registry stays small by construction: any condition the protocol already names belongs to the protocol, not here. -Identifiers are permanent: once published, a code MUST NOT be reused or redefined. Codes MAY be added later; a consumer MUST tolerate an unrecognized `A2ACLI_ERR_*` value and SHOULD fall back to the exit code. - -### E.1 Core codes (required) - -A conformant tool MUST be able to emit these. Together they cover every exit code, so a caller can always act on the result. +A consumer MUST tolerate an unrecognized `A2ACLI_ERR_*` value and SHOULD fall back to the exit code. Codes MAY be added; a published code MUST NOT be reused for a different meaning. Renaming follows the same rule as requirement identifiers (§3.3). | Code | Meaning | Exit | | --- | --- | --- | | `A2ACLI_ERR_USAGE` | Invalid arguments, flags, or flag combination | 2 | -| `A2ACLI_ERR_UNREACHABLE` | Agent could not be reached — DNS, connection, TLS, or no Agent Card | 3 | +| `A2ACLI_ERR_CARD_NOT_FOUND` | The `--agent-card` reference could not be resolved to a card | 3 | +| `A2ACLI_ERR_CARD_INVALID` | Card fetched or read but malformed or schema-invalid | 1 | +| `A2ACLI_ERR_UNREACHABLE` | Agent could not be reached — DNS, connection, or TLS | 3 | | `A2ACLI_ERR_AUTH_REQUIRED` | Credentials required but not supplied | 4 | | `A2ACLI_ERR_AUTH_FAILED` | Credentials supplied but rejected | 4 | -| `A2ACLI_ERR_TASK_FAILED` | Task ended unsuccessfully (`FAILED`, or `REJECTED` if not distinguished) | 5 | +| `A2ACLI_ERR_TASK_FAILED` | Task ended unsuccessfully (`FAILED` or `REJECTED`) | 5 | | `A2ACLI_ERR_INPUT_REQUIRED` | Task needs caller input in a non-interactive run | 6 | | `A2ACLI_ERR_TIMEOUT` | `--timeout` expired before a terminal state | 7 | | `A2ACLI_ERR_INTERNAL` | Unexpected tool-side failure, or any condition with no better code | 1 | -### E.2 Extended codes (optional) - -Use when the tool can distinguish the case. Each refines a core code; if unsupported, report the core code shown in brackets. - -| Code | Meaning | Refines | Exit | -| --- | --- | --- | --- | -| `A2ACLI_ERR_CARD_NOT_FOUND` | No Agent Card at the well-known location or given URL | [`UNREACHABLE`] | 3 | -| `A2ACLI_ERR_CARD_INVALID` | Agent Card fetched but malformed or schema-invalid | [`INTERNAL`] | 1 | -| `A2ACLI_ERR_TASK_NOT_FOUND` | Referenced `taskId` does not exist | [`INTERNAL`] | 1 | -| `A2ACLI_ERR_TASK_REJECTED` | Task reached `REJECTED` rather than `FAILED` | [`TASK_FAILED`] | 5 | -| `A2ACLI_ERR_TASK_NOT_CANCELABLE` | Task cannot be canceled in its current state | [`INTERNAL`] | 1 | -| `A2ACLI_ERR_CONTEXT_MISMATCH` | `contextId` and `taskId` do not correspond | [`INTERNAL`] | 1 | -| `A2ACLI_ERR_CAPABILITY_UNSUPPORTED` | Agent does not advertise a required capability (e.g. streaming) | [`INTERNAL`] | 1 | -| `A2ACLI_ERR_TRANSPORT_UNSUPPORTED` | No transport in common between tool and Agent Card | [`INTERNAL`] | 1 | -| `A2ACLI_ERR_VERSION_UNSUPPORTED` | Agent rejected the signaled protocol version | [`INTERNAL`] | 1 | -| `A2ACLI_ERR_EXTENSION_REQUIRED` | Agent requires an extension the tool does not support | [`INTERNAL`] | 1 | -| `A2ACLI_ERR_STREAM_INTERRUPTED` | Stream ended before a terminal state and could not be resumed | [`INTERNAL`] | 1 | +Task states are not protocol errors — a task reaching `FAILED`, or a run stopping at `INPUT_REQUIRED`, is a normal protocol outcome that the CLI classifies so a shell can act on it. That is why those two codes live here rather than in the protocol's set. From ab45ec347fb34371caaff1b312dfe6b1e3760f77 Mon Sep 17 00:00:00 2001 From: Sampath Kumar Date: Tue, 11 Aug 2026 13:12:48 +0000 Subject: [PATCH 10/31] Require three exit statuses and reserve the rest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scheme required eight exit codes. No tool surveyed emits more than three, and requiring the other five made a Tier 1 requirement that nothing could satisfy — which says the obligation was wrong, not that the vocabulary was. The number of statuses was never the fundamental. What matters is that the status is meaningful and agrees with the error reported. How finely a tool subdivides failure is an elaboration, and it is one that expands safely: adding a status later leaves a caller testing for non-zero unaffected, while removing one breaks anyone who tested for it. Starting small is therefore the direction that does not need reversing. 0, 1 and 2 are required. 3 through 7 keep their meanings and are reserved, so no tool can define a conflicting 3, but a tool that does not implement one reports 1 in its place. Whatever a tool emits must agree with the error it reported. Appendix E's exit column is read the same way, and EXIT_001 now checks the required three plus the correct use of any reserved status. The effect on measurement is the point: one of the three tools tested already satisfies the requirement as restated, and the two that do not now fail for a specific reason — usage errors exit 1 where the convention is 2 — rather than for declining to implement five codes nobody implements. --- specification/COMPLIANCE.md | 4 ++-- specification/SPEC.md | 25 ++++++++++++++++++------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/specification/COMPLIANCE.md b/specification/COMPLIANCE.md index 0792331..75fff0d 100644 --- a/specification/COMPLIANCE.md +++ b/specification/COMPLIANCE.md @@ -10,7 +10,7 @@ This file is also the **authoritative registry of requirement identifiers** (`SP **On authentication.** Authentication, security, and compliance are large topics that need more careful treatment than a checklist row can give them. The `A2ACLI_AUTH_*` requirements below cover the ground the specification defines today, and they are expected to expand. Treat the current coverage as a starting point, not a complete security review. -**On exit codes.** An exit code is the number a command hands back to the shell when it finishes: `0` means success, anything else signals a failure. It matters because it is the only result a script or CI job gets without parsing output — `a2a-cli send … && deploy.sh` behaves correctly only if the tool exits non-zero when the task actually failed. The check confirms the tool returns a meaningful status rather than always `0`, and that the status agrees with the error it reported. +**On exit codes.** An exit code is the number a command hands back to the shell when it finishes: `0` means success, anything else signals a failure. It matters because it is the only result a script or CI job gets without parsing output — `a2a-cli send … && deploy.sh` behaves correctly only if the tool exits non-zero when the task actually failed. Three statuses are required (`0`, `1`, `2`); the rest are reserved, so a tool that implements them reports more precisely rather than merely more. Record which reserved statuses the tool emits in the notes. **On errors.** Failures come in two layers (`SPEC.md` §9.4): a protocol failure carries the A2A error by name (A2A §3.3.2, mapped per §5.4), and a CLI-local failure carries an `A2ACLI_ERR_*` identifier from Appendix E. A tool that renames protocol errors into a vocabulary of its own does not satisfy `A2ACLI_OUT_004`. @@ -77,7 +77,7 @@ A tier is satisfied only when every requirement in it is `✅`. Tiers are cumula | `A2ACLI_OUT_002` | `--output json` — exactly one document, the **terminal** protocol object rather than an event log, and never switched implicitly to `jsonl` | §9.3, App. B | `<>` | | | `A2ACLI_OUT_003` | `--output jsonl` — one complete JSON object per line, flushed as produced | §9.3, App. B | `<>` | | | `A2ACLI_OUT_004` | Errors are machine-readable and consistent across transports: protocol failures carry the A2A error name, CLI-local failures an `A2ACLI_ERR_*` code | §9.4, App. E | `<>` | | -| `A2ACLI_EXIT_001` | Exit status follows the documented scheme and matches the error code reported | §9.6, Appendix E | `<>` | | +| `A2ACLI_EXIT_001` | Implements the three required exit statuses (`0`, `1`, `2`); any reserved status it emits carries the documented meaning and agrees with the error reported | §9.6, App. E | `<>` | | | `A2ACLI_AUTH_001` | Scriptable credentials — bearer, API key, env equivalents, attached as service parameters; `-H/--header` available separately for any service parameter | §10.1 | `<>` | | | `A2ACLI_TX_001` | Transport selected from the Agent Card, honoring declared preference | §11.1 | `<>` | | | `A2ACLI_TX_002` | Uses the first `supported_interfaces` entry it supports absent a client preference; `--transport` is repeatable and ordered | §11.1, §4.5 | `<>` | | diff --git a/specification/SPEC.md b/specification/SPEC.md index c05c74b..e80c374 100644 --- a/specification/SPEC.md +++ b/specification/SPEC.md @@ -343,21 +343,30 @@ A tool SHOULD also populate the envelope's `hint` field with an actionable next 9.5 When the caller does not wait for completion (`--async` / `--return-immediately` / `--no-wait`), the tool MUST still emit a result object carrying the identifiers required to resume or poll later — at minimum `taskId` and `contextId` (§6.3) — so the caller can query status with `get` at a later time. -9.6 Exit codes. The exit code is the coarse signal for shells and CI; the error code is the precise one for programmatic callers. Every error — whether it carries an A2A error name or an `A2ACLI_ERR_*` identifier — MUST map to exactly one of these. +9.6 Exit codes. The exit code is the coarse signal for shells and CI — the only result a caller gets without parsing output. The error code (§9.4) is the precise one. -`0`, `1` and `2` come free with any modern argument parser, and `3` follows from a failed connection. Codes `4` through `7` require the tool to classify the failure, and each exists so a caller can branch on it — retry on a timeout, prompt on input-required, fail a build on a failed task. +**Required.** A conformant tool MUST implement these: + +| Code | Meaning | +| --- | --- | +| 0 | Success — the operation completed, and any task it created reached a successful terminal state | +| 1 | Failure — any error with no more specific code the tool implements | +| 2 | Usage error — invalid arguments, flags, or flag combination | + +**Reserved.** These identifiers carry the meanings below and MUST NOT be used for any other purpose. A tool MAY implement any of them; where it does not, it MUST report `1` instead. | Code | Meaning | | --- | --- | -| 0 | Success / task completed | -| 1 | Generic failure | -| 2 | Usage error | | 3 | Agent or transport unreachable | | 4 | Authentication required or failed | | 5 | Task failed or rejected | -| 6 | Input required (non-interactive) | +| 6 | Input required, in a non-interactive run | | 7 | Timeout | +Whatever a tool emits MUST agree with the error it reported (§9.4). A run that reports a timeout and exits `5` is non-conformant regardless of which codes it implements. + +A later version of this specification MAY promote reserved codes to required. A tool that implements them early is unaffected by that change, and a caller testing only for a non-zero status is unaffected either way. + --- ## 10. Authentication @@ -507,7 +516,7 @@ While the specification is in Draft, notable revisions are recorded by date; the | Version | Date | Notes | | --- | --- | --- | -| 0.1 (Draft) | 2026-08-11 | Terminology: interaction, not conversation. Transport honours the Agent Card's preference order; `--transport` is repeatable and ordered; version negotiates down only within 1.x. Machine-readable output emits the protocol's own types — Appendix B defines no schema — with `json` as the terminal object and no implicit switch to `jsonl`; `tui` removed and §9.2 pins the `text` floor. The CLI is stateless: no capture-and-replay, no `--continue`; §6.4 keeps configuration only, with a documented precedence. Commands namespaced (`task get`, `task list`), `discover` → `agent-inspect`, `--service-url` and `--card-url` → `--agent-card`. Errors defer to the A2A set (§3.3.2, §5.4); Appendix E reduced to CLI-local conditions. `chat` → Tier 3, `push-config` → Tier 2. Shipping a skill is conditional. Conformance is demonstrated against a live agent rather than the TCK, which validates agents rather than clients. Identifier permanence binds from Proposed. | +| 0.1 (Draft) | 2026-08-11 | Terminology: interaction, not conversation. Transport honours the Agent Card's preference order; `--transport` is repeatable and ordered; version negotiates down only within 1.x. Machine-readable output emits the protocol's own types — Appendix B defines no schema — with `json` as the terminal object and no implicit switch to `jsonl`; `tui` removed and §9.2 pins the `text` floor. The CLI is stateless: no capture-and-replay, no `--continue`; §6.4 keeps configuration only, with a documented precedence. Commands namespaced (`task get`, `task list`), `discover` → `agent-inspect`, `--service-url` and `--card-url` → `--agent-card`. Errors defer to the A2A set (§3.3.2, §5.4); Appendix E reduced to CLI-local conditions. `chat` → Tier 3, `push-config` → Tier 2. Shipping a skill is conditional. Conformance is demonstrated against a live agent rather than the TCK, which validates agents rather than clients. Identifier permanence binds from Proposed. Exit codes split into three required statuses and five reserved ones, so conformance is achievable while the vocabulary stays fixed. | | 0.1 (Draft) | 2026-08-04 | Initial draft. Client-only baseline; Tier 1 normative with Tiers 2–3 outlined; interaction and session handling (§6) and task polling (§7) as first-class; opinionated defaults (§4.5); conformant-versus-official and governance (§1.4, §15). | ## Appendix E — CLI-local error codes (normative) @@ -516,6 +525,8 @@ Values for the `code` field (Appendix B) **when the failure is the CLI's own**. The registry stays small by construction: any condition the protocol already names belongs to the protocol, not here. +The **Exit** column gives the status each code maps to when the tool implements that exit code. Codes `0`, `1` and `2` are required; a tool that does not implement a reserved code reports `1` in its place (§9.6). + A consumer MUST tolerate an unrecognized `A2ACLI_ERR_*` value and SHOULD fall back to the exit code. Codes MAY be added; a published code MUST NOT be reused for a different meaning. Renaming follows the same rule as requirement identifiers (§3.3). | Code | Meaning | Exit | From f76834e87a747ccf575f4ef36028151de49f03e8 Mon Sep 17 00:00:00 2001 From: Sampath Kumar Date: Tue, 11 Aug 2026 14:12:33 +0000 Subject: [PATCH 11/31] Tighten error, output, and continuation contracts from review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-ups from the second external review round, all in SPEC.md: - §5.2: split -H/--header out of the credential flags into a general-purpose service-parameter option, matching §10.1. - §7.2: state that push-notification config is Tier 2 (an ordinary API call); only hosting the receiver is Tier 3. - §9.3, §5.2: reject --stream with -o json as a usage error rather than buffering an event log or switching modes implicitly. - §6.2: a rejected --task-id (not found, terminal, or a non-corresponding context) now fails and creates nothing, instead of warning and starting a new task in an unintended context. --- specification/SPEC.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/specification/SPEC.md b/specification/SPEC.md index e80c374..60a8b8e 100644 --- a/specification/SPEC.md +++ b/specification/SPEC.md @@ -178,12 +178,13 @@ The authoritative list of requirement identifiers is the compliance-report templ | `-a, --agent-card ` | The agent to talk to, given as an Agent Card reference: a host (the well-known path is appended), an explicit card URL (used as-is), or a `file://` path to a local card. | | `--context-id ` | Continue an existing interaction (§6.2). | | `--task-id ` | Continue an existing task (§6.2). | -| `-o, --output ` | Output mode. Default `text` (§4.5, §9.2). `json` emits exactly one document; `jsonl` emits one object per line (§9.3). A tool MUST NOT change the selected mode implicitly. | +| `-o, --output ` | Output mode. Default `text` (§4.5, §9.2). `json` emits exactly one document; `jsonl` emits one object per line (§9.3). A tool MUST NOT change the selected mode implicitly; `--stream` with `json` is a usage error (§9.3). | | `--transport ` | Client transport preference, **repeatable and ordered** (highest first). Overrides the card's preference order (§11.1); a binding absent from the card is skipped. | | `--async` / `--return-immediately` / `--no-wait` | Do not wait; return the task identifiers immediately for later polling (default is to wait, §4.5 / §7.3). | | `--wait` / `--watch` | Block until the task reaches a terminal or interrupted state. This is the default for `send` (§4.5); stating it explicitly overrides a configured default. On `get` it turns the one-shot read into a poll loop (§7.3). | | `--poll-interval ` / `--timeout ` | Polling controls (§7.3). | -| `--bearer ` / `--api-key ` / `-H, --header ` | Credentials (§10.1). | +| `--bearer ` / `--api-key ` | Credentials (§10.1). | +| `-H, --header ` | Add an arbitrary service parameter (e.g. an HTTP header), repeatable; general-purpose, not authentication-specific (§10.1). | | `--a2a-version ` | Protocol version to signal (§11). | | `--env ` | Named profile (Tier 2). | | `-v, --verbose` | **Presentation:** show the full part structure rather than collapsing parts into one representation. | @@ -215,8 +216,8 @@ A conformant tool MUST allow continuation via explicit options: - **`--task-id `** continues an existing task — for example, to respond to a task waiting in `INPUT_REQUIRED` (§7.1). Rules: -- `--task-id` MUST be accompanied by `--context-id`, so the pair can never be mismatched. -- When `--task-id` is supplied, the tool MUST send the message against that task. If the server rejects the identifier — not found, or a terminal-state conflict (A2A §3.1.1) — the tool MUST emit a **warning** naming both the requested and the actual task identifier, then continue in the same context, and MUST carry both identifiers in machine-readable output so the substitution is detectable without reading stderr. It MUST point the caller at `--debug` for the underlying protocol error, and MUST NOT abort: a stale identifier is a routine retry, not a broken invocation. +- `--task-id` MUST be accompanied by `--context-id` (the task's context). Requiring both does not by itself prevent a mismatch — a caller can pair a valid `--task-id` with the wrong `--context-id` — which is exactly why a rejected pair MUST fail rather than be reconciled (below). +- When `--task-id` is supplied, the tool MUST send the message against that task. If the server rejects the identifier — not found, a terminal-state conflict (A2A §3.1.1), or a `--context-id` that does not correspond to the task — the tool MUST surface the protocol error, exit non-zero (§9.6), and **MUST NOT create a new task**. Silently starting a fresh task would risk writing into a context the caller did not intend; a rejected identifier is an error to surface, not a condition to work around. The tool MUST point the caller at `--debug` for the underlying protocol error. - When only `--context-id` is supplied, the tool sends a message under that context which MAY return a message or Task - Both supplied is the normal case for task continuation; the tool MUST pass them through unchanged. - Interactive `chat` (Tier 2) MUST carry the `contextId` — and the active `taskId` while a task is interrupted — across turns automatically. @@ -258,7 +259,7 @@ Task states: `SUBMITTED`, `WORKING`, `INPUT_REQUIRED`, `AUTH_REQUIRED`, `COMPLET ### 7.2 Update-delivery mechanisms -A2A provides three ways to observe task progress (A2A §3.5). A conformant tool MUST implement **polling**, SHOULD implement **streaming**, and MAY implement **push notifications** (Tier 3): +A2A provides three ways to observe task progress (A2A §3.5). A conformant tool MUST implement **polling**, SHOULD implement **streaming**, and MAY implement **push notifications**. Managing push-notification *configuration* is an ordinary API call (`push-config`, Tier 2); only *hosting the receiver* is Tier 3: 1. **Streaming (SSE)** — live status/artifact events; the first event MUST be the `Task`. Available only when the Agent Card advertises the streaming capability. 2. **Polling** — repeated `get` until a terminal or interrupted state. Always available; the REQUIRED fallback when streaming is unsupported or a connection drops. @@ -324,7 +325,7 @@ A conformant tool MUST support **both** machine-readable modes. They serve diffe | **`json`** | Exactly **one** complete JSON document — the terminal protocol object — written once, when the result is known | The caller wants the outcome in a single parse — the common scripting case | | **`jsonl`** | **One JSON object per line** ([JSON Lines](https://jsonlines.org/)), flushed as each event occurs | The caller consumes progress incrementally — streaming agents, and agentic apps/harnesses that render or act on partial output | -- **`json` MUST be a single document**: even when the underlying interaction streams, the tool MUST emit exactly one object — the **terminal** protocol object (the final `Task`, or the `Message` where no task was created), never a concatenation of events. A `json` consumer can always `JSON.parse` stdout in one shot, and the size is bounded by the task rather than by how many events it produced. +- **`json` MUST be a single document** — exactly one object, the **terminal** protocol object (the final `Task`, or the `Message` where no task was created), never a concatenation of events. Because `json` cannot represent a live event stream, **`--stream` together with `-o json` MUST be rejected as a usage error** (exit 2, §9.6), directing the caller to `-o jsonl`; the tool MUST NOT silently buffer events or switch modes. A blocking or one-shot invocation (the default, §4.5) still emits `json` normally — one terminal object, whether the tool streamed or polled internally. A `json` consumer can always `JSON.parse` stdout in one shot. - **`jsonl` MUST stream**: each line MUST be a complete, independently parseable JSON object terminated by a newline, flushed as it is produced so a reader can consume the stream incrementally. Lines MUST NOT be pretty-printed across multiple physical lines. - If a tool cannot stream a given interaction (streaming unsupported by the agent, or a one-shot command such as `cancel`), `jsonl` MUST still be honored by emitting the applicable object(s), one per line — a single-line result is valid JSONL. - Both modes MUST emit the A2A protocol's own response types (Appendix B). A tool MUST NOT define a substitute schema, and MUST NOT change the selected mode implicitly — a caller that asked for `json` and received `jsonl` will parse the first line and treat it as the whole result, failing silently with a plausible answer. From 85238416269d3d6f046fa8ca909cbd3958ee942f Mon Sep 17 00:00:00 2001 From: Sampath Kumar Date: Tue, 11 Aug 2026 16:10:01 +0000 Subject: [PATCH 12/31] Revise output model, flags, and distribution from second review round MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Output format is `-o `; `--stream` selects live delivery, rendering events under `text` and emitting JSONL under `json`. `json` stays a single document unless `--stream` is set, and `--stream` must be explicit (never from config, env, or a TTY). Supersedes the earlier `--stream` + `-o json` usage-error. - Add `-v/--version`; `--verbose` keeps only its long form. - Drop named profiles and `--env`: configuration resolves through an environment variable and local/global files with fixed precedence, keeping the CLI stateless. - Artifacts returned by the server MUST be rendered, never stripped. - Reserve `---