# `AgentSessionManager.Ports.ProviderAdapter`
[🔗](https://github.com/nshkrdotcom/agent_session_manager/blob/v0.8.0/lib/agent_session_manager/ports/provider_adapter.ex#L1)

Port (interface) for AI provider adapters.

This behaviour defines the contract that all provider adapter implementations
must fulfill. It follows the ports and adapters pattern, allowing different
AI providers (Anthropic, OpenAI, etc.) to be swapped without changing the
core business logic.

## Design Principles

- **Provider-agnostic**: Core logic doesn't depend on provider specifics
- **Capability-based**: Adapters declare what they support
- **Event-driven**: Execution emits normalized events via callbacks
- **Cancellable**: Long-running operations can be cancelled

## Implementation Requirements

Implementations must:

1. Return a unique provider name
2. Declare supported capabilities
3. Execute runs and emit events via callback
4. Support cancellation of in-progress runs
5. Validate provider-specific configuration

## Event Emission

During `execute/4`, adapters should call the `:event_callback` option (if provided)
with normalized event maps containing:

- `:type` - Event type (e.g., `:run_started`, `:message_received`)
- `:session_id` - The session ID
- `:run_id` - The run ID
- `:data` - Event-specific payload
- `:timestamp` - When the event occurred

## Usage

Adapters are typically used through the SessionManager:

    # The SessionManager handles adapter lifecycle
    {:ok, manager} = SessionManager.start_link(
      adapter: MyAdapter,
      adapter_opts: [api_key: "..."]
    )

    # Or directly for testing
    {:ok, adapter} = MyAdapter.start_link(api_key: "...")
    {:ok, capabilities} = ProviderAdapter.capabilities(adapter)
    {:ok, result} = ProviderAdapter.execute(adapter, run, session, event_callback: fn e -> ... end)

# `adapter`

```elixir
@type adapter() :: GenServer.server() | pid() | atom() | module()
```

# `execute_opts`

```elixir
@type execute_opts() :: [
  event_callback: (map() -&gt; any()),
  timeout: pos_integer() | :unbounded | :infinity | String.t()
]
```

# `run_result`

```elixir
@type run_result() :: %{output: map(), token_usage: map(), events: [map()]}
```

# `cancel`

```elixir
@callback cancel(adapter(), String.t()) ::
  {:ok, String.t()} | {:error, AgentSessionManager.Core.Error.t()}
```

Cancels an in-progress run.

Providers should attempt to gracefully cancel the run. After cancellation,
the run should emit a `:run_cancelled` event.

## Parameters

- `adapter` - The adapter instance
- `run_id` - The ID of the run to cancel

## Returns

- `{:ok, run_id}` - Cancellation initiated (run will emit cancelled event)
- `{:error, Error.t()}` - Cancellation failed

## Examples

    iex> MyAdapter.cancel(adapter, "run_123")
    {:ok, "run_123"}

# `capabilities`

```elixir
@callback capabilities(adapter()) ::
  {:ok, [AgentSessionManager.Core.Capability.t()]}
  | {:error, AgentSessionManager.Core.Error.t()}
```

Returns the list of capabilities supported by this provider.

Capabilities define what the provider can do - tools, resources,
sampling modes, etc. The SessionManager uses this to validate
that required capabilities are available before starting runs.

## Returns

- `{:ok, [Capability.t()]}` - List of supported capabilities
- `{:error, Error.t()}` - If capabilities cannot be determined

## Examples

    iex> MyAdapter.capabilities(adapter)
    {:ok, [
      %Capability{name: "chat", type: :tool, enabled: true},
      %Capability{name: "sampling", type: :sampling, enabled: true}
    ]}

# `execute`

```elixir
@callback execute(
  adapter(),
  AgentSessionManager.Core.Run.t(),
  AgentSessionManager.Core.Session.t(),
  execute_opts()
) :: {:ok, run_result()} | {:error, AgentSessionManager.Core.Error.t()}
```

Executes a run against the AI provider.

This is the main execution entry point. The adapter should:

1. Emit a `:run_started` event
2. Send the request to the provider
3. Emit events as responses come in (`:message_received`, `:tool_call_started`, etc.)
4. Emit `:run_completed` or `:run_failed` when done
5. Return the final result

## Parameters

- `adapter` - The adapter instance
- `run` - The run to execute (contains input, session_id, etc.)
- `session` - The parent session (contains context, metadata)
- `opts` - Execution options:
  - `:event_callback` - Function called for each event emitted
  - `:timeout` - Maximum execution time in milliseconds

## Returns

- `{:ok, result}` - Execution completed successfully
  - `result.output` - The final output from the provider
  - `result.token_usage` - Token usage statistics
  - `result.events` - All events emitted during execution
- `{:error, Error.t()}` - Execution failed

## Examples

    iex> callback = fn event -> Logger.info("Event: #{inspect(event)}") end
    iex> MyAdapter.execute(adapter, run, session, event_callback: callback)
    {:ok, %{
      output: %{content: "Hello!"},
      token_usage: %{input_tokens: 10, output_tokens: 20},
      events: [...]
    }}

# `name`

```elixir
@callback name(adapter()) :: String.t()
```

Returns the unique name of this provider.

This name is used for logging, metrics, and identifying the provider
in multi-provider configurations.

## Examples

    iex> MyAdapter.name(adapter)
    "anthropic"

# `validate_config`

```elixir
@callback validate_config(adapter() | module(), map()) ::
  :ok | {:error, AgentSessionManager.Core.Error.t()}
```

Validates provider-specific configuration.

This is called before the adapter starts to ensure all required
configuration is present and valid.

## Parameters

- `adapter` - The adapter instance (or module for static validation)
- `config` - Configuration map to validate

## Returns

- `:ok` - Configuration is valid
- `{:error, Error.t()}` - Configuration is invalid

## Examples

    iex> MyAdapter.validate_config(adapter, %{api_key: "sk-..."})
    :ok

    iex> MyAdapter.validate_config(adapter, %{})
    {:error, %Error{code: :validation_error, message: "api_key is required"}}

# `cancel`

```elixir
@spec cancel(adapter(), String.t()) ::
  {:ok, String.t()} | {:error, AgentSessionManager.Core.Error.t()}
```

Cancels a run.

# `capabilities`

```elixir
@spec capabilities(adapter()) ::
  {:ok, [AgentSessionManager.Core.Capability.t()]}
  | {:error, AgentSessionManager.Core.Error.t()}
```

Returns the list of capabilities.

# `execute`

```elixir
@spec execute(
  adapter(),
  AgentSessionManager.Core.Run.t(),
  AgentSessionManager.Core.Session.t(),
  execute_opts()
) :: {:ok, run_result()} | {:error, AgentSessionManager.Core.Error.t()}
```

Executes a run.

# `name`

```elixir
@spec name(adapter()) :: String.t()
```

Returns the provider name.

# `resolve_execute_timeout`

```elixir
@spec resolve_execute_timeout(keyword()) :: pos_integer()
```

Resolves execute call timeout from options, including a grace period for
asynchronous result handoff back to callers.

# `validate_config`

```elixir
@spec validate_config(adapter() | module(), map()) ::
  :ok | {:error, AgentSessionManager.Core.Error.t()}
```

Validates configuration.

---

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