## Quick Summary
The `client` directory provides a Python-based client library for Agent-to-Agent (A2A) communication. It allows developers to discover remote agent capabilities via an "Agent Card" and then interact with that agent by sending tasks, receiving streaming responses, and managing the task lifecycle (getting status, cancelling, setting callbacks).

## Files Overview
- `__init__.py`: Exposes the primary `A2AClient` and `A2ACardResolver` classes for easy importing.
- `card_resolver.py`: Contains the `A2ACardResolver` class, used to discover and fetch an agent's capabilities from a well-known endpoint.
- `client.py`: Contains the main `A2AClient` class for all communication with a remote agent, including sending tasks and managing them.

## Developer API Reference

### __init__.py
**Purpose:** This file makes the main client classes available directly under the `client` package, simplifying imports for developers.
**Import:** `from src.solace_agent_mesh.common.client import A2AClient, A2ACardResolver`

**Constants/Variables:**
- `__all__: list[str]` - A list of the public objects that are exported from this module: `["A2AClient", "A2ACardResolver"]`.

---

### card_resolver.py
**Purpose:** This file provides a utility to resolve and fetch an agent's "Agent Card". The Agent Card is a JSON file that describes the agent's capabilities, its endpoint URL, and other metadata.
**Import:** `from src.solace_agent_mesh.common.client import A2ACardResolver`

**Classes:**
- `A2ACardResolver(base_url: str, agent_card_path: str = "/.well-known/agent.json")` - A client to discover and fetch an agent's capability card.
  - `get_agent_card() -> AgentCard` - Makes an HTTP GET request to the constructed agent card URL, parses the JSON response, and returns it as an `AgentCard` object. Raises `A2AClientHTTPError` on network/status errors and `A2AClientJSONError` on parsing errors.

**Usage Examples:**
```python
from src.solace_agent_mesh.common.client import A2ACardResolver
from src.solace_agent_mesh.common.types import AgentCard, A2AClientHTTPError

# --- Example 1: Standard usage ---
# Create a resolver for an agent hosted at a specific domain
resolver = A2ACardResolver(base_url="https://some-agent.ai")

try:
    # Fetch the agent's capability card from the default path
    # (https://some-agent.ai/.well-known/agent.json)
    agent_card: AgentCard = resolver.get_agent_card()
    print(f"Successfully fetched card for agent: {agent_card.name}")
    print(f"Agent API URL: {agent_card.url}")
    print(f"Supported capabilities: {agent_card.capabilities}")

except A2AClientHTTPError as e:
    print(f"Error fetching agent card: {e.status_code} - {e.message}")


# --- Example 2: Using a custom path for the agent card ---
custom_path_resolver = A2ACardResolver(
    base_url="https://another-agent.com",
    agent_card_path="/api/v1/agent-info.json"
)
# This will fetch from https://another-agent.com/api/v1/agent-info.json
custom_agent_card = custom_path_resolver.get_agent_card()
print(f"Agent name from custom path: {custom_agent_card.name}")
```

---

### client.py
**Purpose:** This file contains the core `A2AClient`, which is used to communicate with a remote agent's API endpoint. It handles sending various types of JSON-RPC requests for task management. All methods are asynchronous.
**Import:** `from src.solace_agent_mesh.common.client import A2AClient`

**Classes:**
- `A2AClient(agent_card: AgentCard = None, url: str = None)` - The main client for interacting with a remote agent. You must provide either an `AgentCard` object (from `A2ACardResolver`) or a direct `url` string to its API endpoint.
  - `async send_task(self, payload: dict[str, Any]) -> SendTaskResponse` - Sends a task to the agent for processing. The `payload` should contain the action and its parameters. Returns a response typically containing a `task_id`.
  - `async send_task_streaming(self, payload: dict[str, Any]) -> AsyncIterable[SendTaskStreamingResponse]` - Sends a task that is expected to return a stream of events (Server-Sent Events). The `payload` is the same as `send_task`. Returns an async iterator that yields response chunks as they arrive.
  - `async get_task(self, payload: dict[str, Any]) -> GetTaskResponse` - Retrieves the current status and/or result of a previously submitted task. The `payload` must contain the `task_id`.
  - `async cancel_task(self, payload: dict[str, Any]) -> CancelTaskResponse` - Requests the cancellation of a running task. The `payload` must contain the `task_id`.
  - `async set_task_callback(self, payload: dict[str, Any]) -> SetTaskPushNotificationResponse` - Sets a callback URL for a specific task. The agent will send a notification to this URL upon task completion. The `payload` must contain the `task_id` and `callback_url`.
  - `async get_task_callback(self, payload: dict[str, Any]) -> GetTaskPushNotificationResponse` - Retrieves the currently configured callback URL for a task. The `payload` must contain the `task_id`.

**Usage Examples:**
```python
import asyncio
from src.solace_agent_mesh.common.client import A2AClient, A2ACardResolver

# Assume these types are available for type hinting
# from src.solace_agent_mesh.common.types import SendTaskResponse, SendTaskStreamingResponse

async def main():
    # First, discover the agent's capabilities and endpoint URL
    resolver = A2ACardResolver(base_url="https://some-agent.ai")
    agent_card = resolver.get_agent_card()

    # --- Initialization ---
    # Method 1: Initialize client using the discovered AgentCard
    client = A2AClient(agent_card=agent_card)

    # Method 2: Initialize client with a direct URL (if known)
    # client = A2AClient(url="https://some-agent.ai/api/v1/a2a")

    # --- Send a simple task ---
    print("--- Sending a simple task ---")
    task_payload = {"action": "summarize_text", "text": "A long article..."}
    send_response = await client.send_task(payload=task_payload)
    task_id = send_response.result.task_id
    print(f"Task created with ID: {task_id}")

    # --- Get task status ---
    print("\n--- Checking task status ---")
    status_response = await client.get_task(payload={"task_id": task_id})
    print(f"Task status: {status_response.result.status}")
    
    # --- Send a streaming task ---
    print("\n--- Sending a streaming task ---")
    stream_payload = {"action": "generate_story", "prompt": "A robot who discovers music"}
    async for chunk in client.send_task_streaming(payload=stream_payload):
        # Each chunk is a SendTaskStreamingResponse object
        print(f"Received stream chunk: {chunk.result.content_chunk}")

    # --- Set a callback URL for the task ---
    print("\n--- Setting a callback URL ---")
    await client.set_task_callback(
        payload={"task_id": task_id, "callback_url": "https://my-app.com/webhook"}
    )
    print("Callback URL set.")

    # --- Cancel a task ---
    print("\n--- Cancelling a task ---")
    cancel_response = await client.cancel_task(payload={"task_id": task_id})
    print(f"Task cancellation requested. Success: {cancel_response.result.cancelled}")


if __name__ == "__main__":
    # Note: In a real application, you would use a running event loop.
    # This example assumes the agent endpoint is available and working.
    # To run this, you would need a live agent to connect to.
    # asyncio.run(main())
    print("Developer guide example executed.")

```