# `Jido.Chat.StateAdapter`
[🔗](https://github.com/agentjido/jido_chat/blob/v1.0.0/lib/jido/chat/state_adapter.ex#L1)

Behavior and helpers for pluggable chat state storage.

State adapters own subscriptions, dedupe windows, and per-thread / per-channel
state maps. The default adapter keeps everything in memory, but adapters may
persist state elsewhere as long as they can expose a normalized snapshot.

# `dedupe_key`

```elixir
@type dedupe_key() :: {atom(), String.t()}
```

# `lock_result`

```elixir
@type lock_result() :: :acquired | :queued | :debounced | :busy
```

# `release_result`

```elixir
@type release_result() :: {:released, [map()]} | {:error, :not_owner}
```

# `snapshot`

```elixir
@type snapshot() :: %{
  subscriptions: MapSet.t(String.t()),
  dedupe: MapSet.t(dedupe_key()),
  dedupe_order: [dedupe_key()],
  thread_state: %{optional(String.t()) =&gt; map()},
  channel_state: %{optional(String.t()) =&gt; map()},
  locks: %{optional(String.t()) =&gt; map()},
  pending_locks: %{optional(String.t()) =&gt; [map()]}
}
```

# `state`

```elixir
@type state() :: term()
```

# `channel_state`

```elixir
@callback channel_state(state(), String.t()) :: map()
```

# `duplicate?`

```elixir
@callback duplicate?(state(), dedupe_key()) :: boolean()
```

# `force_release_lock`

```elixir
@callback force_release_lock(state(), String.t()) :: {{:released, [map()]}, state()}
```

# `init`

```elixir
@callback init(
  snapshot(),
  keyword()
) :: state()
```

# `lock`

```elixir
@callback lock(state(), String.t(), String.t(), atom(), map()) :: {lock_result(), state()}
```

# `mark_dedupe`

```elixir
@callback mark_dedupe(state(), dedupe_key(), pos_integer()) :: state()
```

# `put_channel_state`

```elixir
@callback put_channel_state(state(), String.t(), map()) :: state()
```

# `put_thread_state`

```elixir
@callback put_thread_state(state(), String.t(), map()) :: state()
```

# `release_lock`

```elixir
@callback release_lock(state(), String.t(), String.t()) :: {release_result(), state()}
```

# `snapshot`

```elixir
@callback snapshot(state()) :: snapshot() | map()
```

# `subscribe`

```elixir
@callback subscribe(state(), String.t()) :: state()
```

# `subscribed?`

```elixir
@callback subscribed?(state(), String.t()) :: boolean()
```

# `thread_state`

```elixir
@callback thread_state(state(), String.t()) :: map()
```

# `unsubscribe`

```elixir
@callback unsubscribe(state(), String.t()) :: state()
```

# `channel_state`

```elixir
@spec channel_state(module(), state(), String.t()) :: map()
```

Returns channel state map from adapter-managed state.

# `default_snapshot`

```elixir
@spec default_snapshot() :: snapshot()
```

Returns the canonical empty snapshot.

# `duplicate?`

```elixir
@spec duplicate?(module(), state(), dedupe_key()) :: boolean()
```

Returns true when a message dedupe key has already been seen.

# `force_release_lock`

```elixir
@spec force_release_lock(module(), state(), String.t()) ::
  {{:released, [map()]}, state()}
```

Force-releases a lock regardless of owner and returns pending entries.

# `init`

```elixir
@spec init(module(), map(), keyword()) :: state()
```

Initializes adapter state from a normalized snapshot.

# `lock`

```elixir
@spec lock(module(), state(), String.t(), String.t(), atom(), map()) ::
  {lock_result(), state()}
```

Attempts to acquire a concurrency lock for the given key and owner.

# `mark_dedupe`

```elixir
@spec mark_dedupe(module(), state(), dedupe_key(), pos_integer()) :: state()
```

Records a new dedupe key and trims state to the requested limit.

# `normalize_snapshot`

```elixir
@spec normalize_snapshot(map()) :: snapshot()
```

Normalizes maps, lists, and map-sets into the canonical state snapshot shape.

# `put_channel_state`

```elixir
@spec put_channel_state(module(), state(), String.t(), map()) :: state()
```

Writes channel state map into adapter-managed state.

# `put_thread_state`

```elixir
@spec put_thread_state(module(), state(), String.t(), map()) :: state()
```

Writes thread state map into adapter-managed state.

# `release_lock`

```elixir
@spec release_lock(module(), state(), String.t(), String.t()) ::
  {release_result(), state()}
```

Releases a held lock and returns any queued/debounced pending entries.

# `snapshot`

```elixir
@spec snapshot(module(), state()) :: snapshot()
```

Returns a normalized snapshot for adapter-managed state.

# `subscribe`

```elixir
@spec subscribe(module(), state(), String.t()) :: state()
```

Adds a subscribed thread id to adapter-managed state.

# `subscribed?`

```elixir
@spec subscribed?(module(), state(), String.t()) :: boolean()
```

Returns true when the thread is subscribed in adapter-managed state.

# `thread_state`

```elixir
@spec thread_state(module(), state(), String.t()) :: map()
```

Returns thread state map from adapter-managed state.

# `unsubscribe`

```elixir
@spec unsubscribe(module(), state(), String.t()) :: state()
```

Removes a subscribed thread id from adapter-managed state.

---

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