Behaviour for output destinations that receive rendered text from a renderer.
A sink writes rendered output to a specific destination: terminal, file, JSON log, callback function, etc. Sinks receive iodata and decide how to write it.
Implementing a Sink
defmodule MySink do
@behaviour AgentSessionManager.Rendering.Sink
@impl true
def init(opts), do: {:ok, %{device: opts[:device] || :stdio}}
@impl true
def write(iodata, state) do
IO.write(state.device, iodata)
{:ok, state}
end
@impl true
def write_event(_event, _iodata, state), do: {:ok, state}
@impl true
def flush(state), do: {:ok, state}
@impl true
def close(state), do: :ok
endRendered vs Raw Output
Sinks receive output through two channels:
write/2— Rendered text from the renderer. Used for human-readable output.write_event/3— The raw event plus rendered text. Used by sinks that need the structured event data (e.g. JSONL loggers that serialize events, not text).
Summary
Callbacks
Close the sink and release resources.
Flush any buffered output.
Initialize sink state from options.
Write rendered iodata to the output destination.
Write the raw event alongside its rendered form. Called for every event.
Types
Callbacks
@callback close(state()) :: :ok
Close the sink and release resources.
Flush any buffered output.
Initialize sink state from options.
Write rendered iodata to the output destination.
@callback write_event(event :: map(), iodata(), state()) :: {:ok, state()} | {:error, term(), state()}
Write the raw event alongside its rendered form. Called for every event.
Sinks that only care about rendered text can delegate to write/2 or no-op.
Sinks that need structured data (JSONL, callback) use the event directly.