Raxol. AgentClientProtocol. Session. Emitter behaviour
(Raxol Agent Client Protocol v0.1.0)
Copy Markdown
View Source
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)— asession/update(in-turnpost_updateand out-of-turnset_mode'scurrent_mode_update).turn_started(session_state, prompt_req, turn_id)— a turn begins; the per-session ordinalturn_idis 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;:okotherwise (Direct always:ok; no latch).turn_completed(session_state, rendered, turn_id)— a turn ends;renderedis the SINGLE-render-site value that ALSO becomes the prompt response (both are built from onerender(outcome)call).
session_state is the %Raxol.AgentClientProtocol.Session{} struct; an emitter
reads conn/conn_mod/session_id/journal from it.
Summary
Callbacks
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.
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.
Callbacks
@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.
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.
@callback turn_completed( session_state :: struct(), rendered :: term(), turn_id :: pos_integer() ) :: :ok
@callback turn_started( session_state :: struct(), prompt_req :: term(), turn_id :: pos_integer() ) :: :ok | {:error, :turn_in_flight}