# `A2A.Client`
[🔗](https://github.com/actioncard/a2a-elixir/blob/main/lib/a2a/client.ex#L2)

HTTP client for consuming remote A2A agents.

Provides discovery, synchronous messaging, SSE streaming, and task
management using the A2A JSON-RPC protocol over HTTP.

## Quick Start

    # Discover an agent
    {:ok, card} = A2A.Client.discover("https://agent.example.com")

    # Create a client and send a message
    client = A2A.Client.new(card)
    {:ok, task} = A2A.Client.send_message(client, "Hello!")

    # Stream a response
    {:ok, stream} = A2A.Client.stream_message(client, "Count to 5")
    Enum.each(stream, &IO.inspect/1)

## Convenience Overloads

All functions that accept a `%A2A.Client{}` also accept a URL string
or `%A2A.AgentCard{}`:

    {:ok, task} = A2A.Client.send_message("https://agent.example.com", "Hello!")
    {:ok, task} = A2A.Client.send_message(card, "Hello!")

## Options

Functions that send messages accept these options:

- `:task_id` — continue an existing task (multi-turn)
- `:context_id` — set the context ID
- `:configuration` — `MessageSendConfiguration` map
- `:metadata` — arbitrary metadata map
- `:headers` — additional HTTP headers
- `:timeout` — HTTP request timeout in ms

## Extensions

Pass `:extensions` to `new/2` to declare A2A protocol extensions this
client supports. Their declared URIs are sent in the `A2A-Extensions`
request header on every call. Use `parse_extensions_header/1` and
`activated/2` on the resulting `Req.Response` to find out which
extensions the server activated.

## Protocol version

Pass `:version` to `new/2` to set the `A2A-Version` request header
sent on every call. Defaults to `A2A.Version.default/0` (`"1.0"`).
Use `version/1` on a `Req.Response` to read the version the server
echoed back.

# `t`

```elixir
@type t() :: %A2A.Client{
  extensions: [A2A.Extension.compiled()],
  req: Req.Request.t(),
  url: String.t()
}
```

# `target`

```elixir
@type target() :: t() | A2A.AgentCard.t() | String.t()
```

# `activated`

```elixir
@spec activated(t(), Req.Response.t()) :: [module()]
```

Returns the configured extension modules whose URI appears in the
server's `A2A-Extensions` response header.

# `cancel_task`

```elixir
@spec cancel_task(target(), String.t(), keyword()) ::
  {:ok, A2A.Task.t()} | {:error, term()}
```

Cancels a task by ID via `CancelTask`.

## Options

- `:headers` — additional HTTP headers
- `:timeout` — HTTP request timeout in ms

## Examples

    {:ok, task} = A2A.Client.cancel_task(client, "tsk-abc123")

# `delete_push_config`

```elixir
@spec delete_push_config(target(), String.t(), String.t(), keyword()) ::
  :ok | {:error, term()}
```

Deletes a push notification config. Idempotent — deleting a config that is
not registered succeeds.

## Options

- `:headers` — additional HTTP headers
- `:timeout` — HTTP request timeout in ms

# `discover`

```elixir
@spec discover(String.t(), keyword()) :: {:ok, A2A.AgentCard.t()} | {:error, term()}
```

Discovers an agent by fetching its agent card.

Sends `GET /.well-known/agent-card.json` and decodes the response
into an `%A2A.AgentCard{}`.

## Options

- `:headers` — additional HTTP headers
- `:timeout` — HTTP request timeout in ms
- `:agent_card_path` — custom discovery path
  (default: `"/.well-known/agent-card.json"`)

## Examples

    {:ok, card} = A2A.Client.discover("https://agent.example.com")
    card.name #=> "my-agent"

# `get_push_config`

```elixir
@spec get_push_config(target(), String.t(), String.t(), keyword()) ::
  {:ok, A2A.PushNotificationConfig.t()} | {:error, term()}
```

Retrieves a push notification config by task ID and config ID.

## Options

- `:headers` — additional HTTP headers
- `:timeout` — HTTP request timeout in ms

# `get_task`

```elixir
@spec get_task(target(), String.t(), keyword()) ::
  {:ok, A2A.Task.t()} | {:error, term()}
```

Retrieves a task by ID via `GetTask`.

## Options

- `:history_length` — number of history entries to include
- `:headers` — additional HTTP headers
- `:timeout` — HTTP request timeout in ms

## Examples

    {:ok, task} = A2A.Client.get_task(client, "tsk-abc123")

# `list_push_configs`

```elixir
@spec list_push_configs(target(), String.t(), keyword()) ::
  {:ok, [A2A.PushNotificationConfig.t()]} | {:error, term()}
```

Lists every push notification config registered for a task.

## Options

- `:headers` — additional HTTP headers
- `:timeout` — HTTP request timeout in ms

# `new`

```elixir
@spec new(A2A.AgentCard.t() | String.t(), keyword()) :: t()
```

Creates a new client struct.

Accepts a URL string or `%A2A.AgentCard{}`. Options are forwarded to
`Req.new/1` for customizing the HTTP client (headers, timeouts, etc.).

## Examples

    client = A2A.Client.new("https://agent.example.com")
    client = A2A.Client.new(card, headers: [{"authorization", "Bearer token"}])

# `parse_extensions_header`

```elixir
@spec parse_extensions_header(Req.Response.t()) :: [String.t()]
```

Parses the `A2A-Extensions` header from a `Req.Response`. Returns the
list of extension URIs the server activated for the corresponding
request, or `[]` if the header is absent.

HTTP headers may appear as a single comma-separated value or as
multiple repeated headers; both are handled.

# `resubscribe`

```elixir
@spec resubscribe(target(), String.t(), keyword()) ::
  {:ok, Enumerable.t()} | {:error, term()}
```

Reattaches to a running task's event stream via `SubscribeToTask`.

Returns `{:ok, stream}` for a task still in progress. The first element is
the task as it stands; subsequent elements are `%A2A.Event.StatusUpdate{}`
structs, and the stream ends when the task reaches a terminal state.

A task that does not exist answers `TaskNotFoundError` and one that has
already finished answers `UnsupportedOperationError` — both come back as
`{:error, %A2A.JSONRPC.Error{}}` rather than an empty stream.

Events produced before the subscription are not replayed, so a caller that
needs the full history should pair this with `get_task/3`.

## Options

- `:history_length` — number of history entries to include in the snapshot
- `:headers` — additional HTTP headers
- `:timeout` — HTTP request timeout in ms

## Examples

    {:ok, stream} = A2A.Client.resubscribe(client, "tsk-abc123")
    Enum.each(stream, &IO.inspect/1)

# `send_message`

```elixir
@spec send_message(target(), A2A.Message.t() | String.t(), keyword()) ::
  {:ok, A2A.Task.t() | A2A.Message.t()} | {:error, term()}
```

Sends a message to an agent via `SendMessage`.

Returns `{:ok, task}` on success, or `{:ok, message}` when the agent
answers out-of-band with a bare `%A2A.Message{}` — `SendMessageResponse`
is a Task/Message oneof, so match on the struct to tell them apart.
Returns `{:error, reason}` on failure. The message can be a string, an
`%A2A.Message{}`, or a list of parts.

## Options

- `:task_id` — continue an existing task
- `:context_id` — set the context ID
- `:configuration` — `MessageSendConfiguration` map
- `:metadata` — arbitrary metadata map
- `:headers` — additional HTTP headers
- `:timeout` — HTTP request timeout in ms

## Examples

    {:ok, task} = A2A.Client.send_message(client, "Hello!")
    {:ok, task} = A2A.Client.send_message(client, "More info", task_id: task.id)

# `set_push_config`

```elixir
@spec set_push_config(target(), A2A.PushNotificationConfig.t(), keyword()) ::
  {:ok, A2A.PushNotificationConfig.t()} | {:error, term()}
```

Registers a push notification config for a task.

The config's `:task_id` names the task; `:id` is assigned by the server
when left `nil`. Returns the stored config.

## Options

- `:headers` — additional HTTP headers
- `:timeout` — HTTP request timeout in ms

## Examples

    config = %A2A.PushNotificationConfig{
      task_id: "tsk-abc123",
      url: "https://example.com/webhook",
      authentication: %{scheme: "Bearer", credentials: "s3cret"}
    }

    {:ok, stored} = A2A.Client.set_push_config(client, config)

# `stream_message`

```elixir
@spec stream_message(target(), A2A.Message.t() | String.t(), keyword()) ::
  {:ok, Enumerable.t()} | {:error, term()}
```

Sends a message and returns a stream of decoded SSE events.

Uses `SendStreamingMessage` to receive server-sent events. Returns
`{:ok, stream}` where the stream yields decoded structs
(`%A2A.Task{}`, `%A2A.Event.StatusUpdate{}`, `%A2A.Event.ArtifactUpdate{}`,
or `%A2A.Message{}`).

## Options

Same as `send_message/3`.

## Examples

    {:ok, stream} = A2A.Client.stream_message(client, "Count to 5")
    Enum.each(stream, fn
      %A2A.Event.StatusUpdate{final: true} -> :done
      event -> IO.inspect(event)
    end)

# `version`

```elixir
@spec version(Req.Response.t()) :: String.t() | nil
```

Returns the negotiated A2A protocol version from the server's
`A2A-Version` response header, or `nil` if the header is absent.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
