# `Agentic.AgentProtocol`

Behaviour for agent communication protocols.

Implement this for backends that communicate via different wire formats,
session management, or execution models.

## Protocol vs Transport

- **Protocol** - High-level agent communication (LLM API vs CLI subprocess)
- **Transport** - Wire-level HTTP implementations (OpenAI, Anthropic, etc.)

Protocols handle session lifecycle, response parsing, and tool execution
delegation. Transports handle the actual HTTP request/response mechanics.

## Implementations

Built-in protocols:
- `Agentic.Protocol.LLM` - Wraps existing callback-based LLM calls
- `Agentic.Protocol.ClaudeCode` - Claude Code CLI via subprocess
- `Agentic.Protocol.OpenCode` - OpenCode CLI via subprocess

## Callbacks

Implement all required callbacks for lifecycle management and message handling.
Optional callbacks provide streaming, cost estimation, and MCP integration.

# `protocol_response`

```elixir
@type protocol_response() :: %{
  optional(:content) =&gt; String.t(),
  optional(:tool_calls) =&gt; [map()],
  optional(:usage) =&gt; %{input: non_neg_integer(), output: non_neg_integer()},
  optional(:stop_reason) =&gt; String.t() | nil,
  optional(:metadata) =&gt; map()
}
```

# `send_opts`

```elixir
@type send_opts() :: [stream: boolean(), timeout_ms: non_neg_integer()]
```

# `session_id`

```elixir
@type session_id() :: binary()
```

# `start_opts`

```elixir
@type start_opts() :: [
  workspace: String.t(),
  user_id: String.t(),
  session_id: String.t(),
  cli_config: map()
]
```

# `available?`

```elixir
@callback available?() :: boolean()
```

Check if protocol is available on the system.

For local agent protocols, checks if CLI binary exists.
For LLM protocols, checks if API credentials are configured.

Default: returns true

# `estimate_cost`

```elixir
@callback estimate_cost(response :: protocol_response()) :: float()
```

Estimate cost for a response.

For subscription-based agents (hourly/daily limits), this estimates
the cost in abstract units. For LLM protocols, uses token counts.

Default: returns 0.0

# `format_messages`

```elixir
@callback format_messages(messages :: [map()], context :: Agentic.Loop.Context.t()) ::
  iodata()
```

Format messages for the wire protocol.

Converts Agentic message format to protocol-specific format.

## Parameters

  - `messages` - List of Agentic messages (%{"role" => ..., "content" => ...})
  - `context` - Agentic context

## Returns

  - `iodata()` - Formatted message(s) ready for wire transmission

# `get_usage`

```elixir
@callback get_usage(session_id()) :: map() | nil
```

Get current usage stats for a session.

Returns usage information for the current billing period.
Useful for displaying progress bars or limits in the UI.

Default: returns nil (no tracking)

# `parse_stream`

```elixir
@callback parse_stream(chunk :: binary()) ::
  {:message, map()} | :partial | :eof | {:error, term()}
```

Parse a streaming chunk into messages.

Called during streaming to convert raw bytes to structured data.
Different protocols have different streaming formats (JSON, JSONL, SSE, etc.)

## Parameters

  - `chunk` - Raw bytes received from the agent

## Returns

  - `{:message, map()}` - Complete message parsed from chunk
  - `:partial` - Incomplete data, need more
  - `:eof` - Stream completed
  - `{:error, reason}` - Parse error

# `resume`

```elixir
@callback resume(session_id(), messages :: [map()], context :: Agentic.Loop.Context.t()) ::
  {:ok, session_id(), protocol_response()} | {:error, term()}
```

Resume an existing session with new messages.

For session-based protocols, continues a paused session. For stateless
protocols, equivalent to `send/3`.

## Parameters

  - `session_id` - Existing session ID from prior start/resume
  - `messages` - New messages to append
  - `context` - Agentic context

## Returns

  - `{:ok, session_id, response}` - Session continued, with response
  - `{:error, reason}` - Failed to resume

# `send`

```elixir
@callback send(session_id(), messages :: [map()], context :: Agentic.Loop.Context.t()) ::
  {:ok, protocol_response()} | {:error, term()}
```

Send messages and receive a response.

The core protocol interaction. Takes conversation history and returns
the agent's response with potential tool calls.

## Parameters

  - `session_id` - Session from `start/2`
  - `messages` - List of message maps with :role and :content keys
  - `context` - Agentic context

## Returns

  - `{:ok, response}` - Successful response
  - `{:error, reason}` - Failed to get response

# `start`

```elixir
@callback start(backend_config :: map(), context :: Agentic.Loop.Context.t()) ::
  {:ok, session_id()} | {:error, term()}
```

Start a new agent session.

Called when a new session begins. For session-based protocols (local agents),
this spawns the subprocess. For stateless protocols (LLM), this may be
a no-op or initialize rate limiting.

## Parameters

  - `backend_config` - Protocol-specific configuration (API keys, CLI paths, etc.)
  - `context` - The Agentic context with metadata, config, and callbacks

## Returns

  - `{:ok, session_id}` - Session started successfully
  - `{:error, reason}` - Failed to start session

# `stop`

```elixir
@callback stop(session_id()) :: :ok | {:error, term()}
```

Stop and cleanup a session.

Gracefully terminates the session, persisting any state needed for resume.

## Parameters

  - `session_id` - Session to stop

## Returns

  - `:ok` - Stopped successfully
  - `{:error, reason}` - Error during cleanup (non-fatal)

# `stream_message`

```elixir
@callback stream_message(
  session_id(),
  chunk :: map(),
  context :: Agentic.Loop.Context.t()
) :: :ok
```

Stream a message chunk to the client.

Called during response streaming to push partial data.
The callback should be invoked from context.callbacks[:on_stream_message].

Default: no-op

# `transport_type`

```elixir
@callback transport_type() :: Agentic.Protocol.transport_type()
```

Return the transport type.

Identifies whether this is an LLM API or local agent protocol.

# `available?`

# `estimate_cost`

# `get_usage`

# `stream_message`

---

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