# `Raxol.AgentClientProtocol.Session.Emitter`
[🔗](https://github.com/DROOdotFOO/raxol-agent/blob/raxol_agent_client_protocol-v0.1.0/packages/raxol_agent_client_protocol/lib/raxol/agent_client_protocol/session.ex#L1015)

The Session emit seam — one injection point through which every
`session/update` and every turn boundary flows.

The base package binds `Emitter.Direct` (`emit/2` calls `Connection.notify`;
the boundary hooks are no-ops). `Emitter.Journal` reroutes updates + turn
boundaries through the single-publisher Writer (`Ext.Journal.Writer`) so every
delivered update is ALSO durable and a reattacher replays an exact
offset-addressed journal.

## Callbacks

  * `emit(session_state, notification)` — a `session/update` (in-turn
    `post_update` and out-of-turn `set_mode`'s `current_mode_update`).
  * `turn_started(session_state, prompt_req, turn_id)` — a turn begins; the
    per-session ordinal `turn_id` is threaded from the Session. Returns
    `{:error, :turn_in_flight}` when a cross-connection turn latch is already held
    for this session_id — the Session then replies busy and runs
    nothing; `:ok` otherwise (Direct always `:ok`; no latch).
  * `turn_completed(session_state, rendered, turn_id)` — a turn ends; `rendered`
    is the SINGLE-render-site value that ALSO becomes the prompt response
    (both are built from one `render(outcome)` call).

`session_state` is the `%Raxol.AgentClientProtocol.Session{}` struct; an emitter
reads `conn`/`conn_mod`/`session_id`/`journal` from it.

# `emit`

```elixir
@callback emit(session_state :: struct(), notification :: struct()) :: :ok
```

# `reap`
*optional* 

```elixir
@callback reap(session_state :: struct()) :: :ok
```

OPTIONAL idle-reap hook (durable-sessions robustness). Called once when the
Session idle-reaps: an emitter that owns a live durable process (the Journal's
single-publisher Writer) STOPS it here so a long-lived node does not accrue a
never-stopped Writer per session. The journal store outlives the Writer, so a
fresh reattach respawns + replays. `Direct` has no such process — this is a
no-op — and any emitter that omits the callback is treated as a no-op.

# `steer_accepted`
*optional* 

```elixir
@callback steer_accepted(session_state :: struct(), ref :: map(), request :: map()) :: :ok
```

OPTIONAL steer-accept hook. Called once per ACCEPTED steer
(never on a reject or a duplicate). `ref` is the accept reference
(`%{turn_id, offset, client_msg_id}`) and `request` is the resolved steer map
(`%{text, expected_turn_id, client_msg_id}`). A durable emitter (`Journal`)
appends one `"steer"` record so a reattacher replays the redirected turn; the
default `Direct` emitter omits this callback (it keeps no journal) and the
Session treats an omitted callback as a no-op.

# `turn_completed`

```elixir
@callback turn_completed(
  session_state :: struct(),
  rendered :: term(),
  turn_id :: pos_integer()
) :: :ok
```

# `turn_started`

```elixir
@callback turn_started(
  session_state :: struct(),
  prompt_req :: term(),
  turn_id :: pos_integer()
) ::
  :ok | {:error, :turn_in_flight}
```

---

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