The unit of agency (ADR 0001): a single GenServer that owns one conversation —
its :role (the Agent configuration), its monotonic seq counter, and the append
to its Log. There is exactly one Session process per session_id, registered in
Pixir.Sessions.Registry.
Recording events
Canonical events go through record/2, which runs the load-bearing sequence inside
the GenServer (so it is serialized): stamp seq → append to the Log → publish on
the bus. The Log is the source of truth, so an event that fails to persist is not
published. Ephemeral events go through emit/2 (publish only — no seq, no Log).
Turns as supervised Tasks
A Turn runs in a Task under Pixir.TurnSupervisor, monitored (not linked) by the
Session. interrupt/1 kills that Task — the load-bearing invariant that makes a Turn
cleanly cancellable without taking the Session down. The Turn body is a 1-arity
function given a context map (%{session_id, workspace, role, fork_root_session_id});
the real tool-loop (build step 7) plugs in here.
Resume
On init/1 the Session folds its existing Log to seed seq (so new canonical events
continue the sequence). History itself is always re-derived from the Log on demand
(history/1), never held as authoritative state (ADR 0003).
Summary
Functions
Returns a specification to start this module under a supervisor.
Publish an ephemeral Event (live display only; never persisted).
Generate a sortable, filename-safe Session id.
Reconstruct History by folding the Log (the source of truth).
Snapshot of Session metadata (id, workspace, role, next seq, turn state).
Kill the currently running Turn's Task, if any.
Record a canonical Event: stamp seq, append to the Log, then publish. Returns the
stamped Event, or a structured error if it was not canonical / failed to persist.
Hysteresis gate for context-pressure warnings (ADR 0020): returns {:ok, :warn}
the first time a (latest checkpoint to_seq, tier) pair is seen and
{:ok, :already_warned} afterwards — no warning spam on consecutive turns for
the same pair. A new compaction checkpoint (different to_seq) or a different
tier re-arms the gate. State is ephemeral process state by design: it is never
logged, and re-warning after a Session restart is acceptable.
Start a Turn: run turn_fun.(ctx) in a supervised Task. Returns {:ok, ref} or
{:error, :busy} if a Turn is already running. If the previous Turn left orphan
tool calls in the Log, Pixir first records fallback tool_result events so Provider
replay stays valid.
Whether a Turn is currently running.
{:via, Registry, …} name for a Session id.
Types
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec emit(String.t(), Pixir.Event.t()) :: :ok
Publish an ephemeral Event (live display only; never persisted).
@spec gen_id() :: String.t()
Generate a sortable, filename-safe Session id.
@spec history(String.t()) :: {:ok, Pixir.Log.history()} | {:error, map()}
Reconstruct History by folding the Log (the source of truth).
Snapshot of Session metadata (id, workspace, role, next seq, turn state).
Kill the currently running Turn's Task, if any.
@spec record(String.t(), Pixir.Event.t()) :: {:ok, Pixir.Event.t()} | {:error, map()}
Record a canonical Event: stamp seq, append to the Log, then publish. Returns the
stamped Event, or a structured error if it was not canonical / failed to persist.
@spec register_pressure_warning(String.t(), integer() | nil, String.t()) :: {:ok, :warn | :already_warned}
Hysteresis gate for context-pressure warnings (ADR 0020): returns {:ok, :warn}
the first time a (latest checkpoint to_seq, tier) pair is seen and
{:ok, :already_warned} afterwards — no warning spam on consecutive turns for
the same pair. A new compaction checkpoint (different to_seq) or a different
tier re-arms the gate. State is ephemeral process state by design: it is never
logged, and re-warning after a Session restart is acceptable.
@spec start_turn(String.t(), (ctx() -> any())) :: {:ok, reference()} | {:error, :busy} | {:error, map()}
Start a Turn: run turn_fun.(ctx) in a supervised Task. Returns {:ok, ref} or
{:error, :busy} if a Turn is already running. If the previous Turn left orphan
tool calls in the Log, Pixir first records fallback tool_result events so Provider
replay stays valid.
Whether a Turn is currently running.
{:via, Registry, …} name for a Session id.