Pixir.Conversation (pixir v0.1.0)

Copy Markdown View Source

The UI-agnostic multi-turn driver (ADR 0008): the conversational loop over Session/Turn/Events, with no presentation. Every front-end — the terminal CLI, a future HTTP/WebSocket tier, an editor extension, or an embedding Elixir app — drives Pixir through this module.

It is a stateless functional module, not a process: the Session GenServer already owns turn state, History, seq, and interrupt (ADR 0001). Per-client state (a socket, a pending permission reply) belongs to the transport tier, never here.

Shape

{:ok, sid} = Conversation.start(workspace: ".")      # new Session
{:ok, sid} = Conversation.start(id: sid)             # resume / reattach
{:ok, ref} = Conversation.send(sid, "do the thing")  # run one Turn (non-blocking)

Observation is the Events bus (ADR 0004), full stop — this module invents no new streaming abstraction. An out-of-process UI subscribes via Pixir.Events.subscribe/1 and forwards each {:pixir_event, event} over its transport as JSON. For in-process callers (tests, an optional terminal presenter) await/2 consumes the bus until the Turn reaches a terminal status, with an optional on_event callback.

Permissions stay injectable (ADR 0006): pass an :asker function through send/3; this module implements no prompting. Async, remote permission decisions are a transport-tier concern (an asker that blocks the Turn while round-tripping over a socket).

Summary

Functions

Block until the current Turn reaches a terminal status, consuming bus events. The caller must already be subscribed (Pixir.Events.subscribe/1) — typically call subscribe, then send, then await.

The Session's History, folded from the Log (pass-through to Session).

Interrupt the running Turn, if any (pass-through to Session).

Run one Turn for prompt in the Session. Non-blocking — returns {:ok, ref} once the Turn task is started ({:error, :busy} if a Turn is already running). Observe progress via the bus, or block with await/2.

Start a conversation, returning its Session id.

Subscribe the calling process to the Session's event bus (pass-through).

Types

session_id()

@type session_id() :: String.t()

Functions

await(session_id, opts \\ [])

@spec await(
  session_id(),
  keyword()
) :: :done | :error | :interrupted | :timeout

Block until the current Turn reaches a terminal status, consuming bus events. The caller must already be subscribed (Pixir.Events.subscribe/1) — typically call subscribe, then send, then await.

Returns :done | :error | :interrupted | :timeout. interrupted is terminal (ADR 0008 — the Renderer's old loop hung on it). Options: :on_event (a 1-arity callback invoked per event, for in-process rendering) and :idle_timeout (ms, default 120_000).

history(session_id)

@spec history(session_id()) :: {:ok, Pixir.Log.history()} | {:error, map()}

The Session's History, folded from the Log (pass-through to Session).

interrupt(session_id)

@spec interrupt(session_id()) :: :ok | {:error, :no_turn}

Interrupt the running Turn, if any (pass-through to Session).

send(session_id, prompt, opts \\ [])

@spec send(session_id(), String.t(), keyword()) ::
  {:ok, reference()} | {:error, :busy}

Run one Turn for prompt in the Session. Non-blocking — returns {:ok, ref} once the Turn task is started ({:error, :busy} if a Turn is already running). Observe progress via the bus, or block with await/2.

Options are passed to Turn.run/3: :permission_mode (default :auto), :asker (default deny), :provider, :provider_opts, :dry_run, :max_iterations.

start(opts \\ [])

@spec start(keyword()) :: {:ok, session_id()} | {:error, map()}

Start a conversation, returning its Session id.

  • no :id — mint a new Session.
  • :idresume a persisted Session (or idempotently reattach if it is already running). A missing Log is a structured :not_found error; a corrupt Log surfaces as a structured error rather than crashing the caller.

Options: :id, :workspace (default cwd), :role (default :build).

subscribe(session_id)

@spec subscribe(session_id()) :: :ok

Subscribe the calling process to the Session's event bus (pass-through).