SuperPi.Event (super_pi v0.1.0)

Copy Markdown View Source

The typed turn event: one schema that every interface renders.

A conversation publishes events on the PubSub topic "conversation:<id>" as {:conversation_event, conversation_id, wire_map}. Before this module each producer built an untyped map and each consumer (Gateway, RPC server, TUI) read it again in its own way. Now a producer builds an event with one function per type (the only sanctioned construction site, each with a @spec), and a consumer reads it through a renderer (SuperPi.Event.Renderer).

There is one struct, not one per type. The wire form is a plain map anyway, and a renderer matches on :type.

Wire form

to_wire/1 gives the flat map that consumers have always read, plus "v" (schema version, 1) and "turn_id" (the id of the turn task, or nil outside a turn). Payload keys stay at the top level, not under "data", so a consumer that matches %{"type" => "text_delta", "delta" => d} keeps working. Both additions are purely additive.

Types

TypePayload keys
turn_startedseq
turn_completedstop_reason, result, seq
turn_errorerror, seq
turn_cancelledseq
text_deltaindex, delta
thinking_deltaindex, delta
toolcall_deltaindex, delta, partial_args, and id and name when the provider gave them
message_completedrole, content, usage
tool_startedtool_call_id, name, arguments, overwritten_keys
tool_blockedtool_call_id, name, reason
tool_completedtool_call_id, name, is_error, output
permission_requestrequest_id, tool_call_id, tool_name, arguments, reason, risk, options
ui_requestrequest_id and the request map (for example kind, questions)
ui_resolvedrequest_id, outcome
state_changedstatus
mode_changedmode, max_mode
stream_resetretry_in_ms
compaction_completedhead_entry_id, summary
usage_updatedinput_tokens, output_tokens, total_input_tokens, total_output_tokens
plan_updatedentries (todo items with id, text, completed)
noticelevel, message
watch_startedseq
subagent_startedchild_conversation_id, role, task
subagent_finishedchild_conversation_id, status, summary

stop_reason of turn_completed is "end_turn". permission_request options carry ACP PermissionOptionKind values in kind: allow_once, allow_always, reject_once, reject_always.

Summary

Functions

Wraps a map that an extension passed to ctx.emit_event into an event. The map must have a "type". Keys that belong to the envelope are taken from the arguments, not the map.

Reads a wire map back into an event. A map from an older producer (no "v", no "turn_id") reads as version 1 with no turn.

The four permission options, with ACP PermissionOptionKind in kind.

A tool call waits for a human decision. request_id answers through SuperPi.UI.Broker.respond/2 with the option_id of one of options.

Publishes an event on the conversation topic.

The wire map of an event: its payload at the top level, plus the envelope keys.

PubSub topic of a conversation.

The event types.

Types

t()

@type t() :: %SuperPi.Event{
  conversation_id: integer() | nil,
  data: map(),
  timestamp_ms: integer(),
  turn_id: integer() | nil,
  type: String.t(),
  v: pos_integer()
}

Functions

compaction_completed(conv, head_entry_id, summary)

@spec compaction_completed(integer(), term(), String.t() | nil) :: t()

from_extension(conversation_id, turn_id, map)

@spec from_extension(integer(), integer() | nil, map()) :: t()

Wraps a map that an extension passed to ctx.emit_event into an event. The map must have a "type". Keys that belong to the envelope are taken from the arguments, not the map.

from_wire(wire)

@spec from_wire(map()) :: t()

Reads a wire map back into an event. A map from an older producer (no "v", no "turn_id") reads as version 1 with no turn.

message_completed(conv, turn, role, content, usage)

@spec message_completed(integer(), integer() | nil, String.t(), [map()], map()) :: t()

mode_changed(conv, mode, max_mode)

@spec mode_changed(integer(), String.t(), String.t() | nil) :: t()

notice(conv, turn, level, message)

@spec notice(integer(), integer() | nil, String.t(), String.t()) :: t()

permission_options()

@spec permission_options() :: [map()]

The four permission options, with ACP PermissionOptionKind in kind.

permission_request(conv, turn, fields)

@spec permission_request(integer(), integer() | nil, map()) :: t()

A tool call waits for a human decision. request_id answers through SuperPi.UI.Broker.respond/2 with the option_id of one of options.

plan_updated(conv, entries)

@spec plan_updated(integer(), [map()]) :: t()

publish(ev)

@spec publish(t()) :: :ok

Publishes an event on the conversation topic.

state_changed(conv, turn, status)

@spec state_changed(integer(), integer() | nil, String.t()) :: t()

stream_reset(conv, turn, retry_in_ms)

@spec stream_reset(integer(), integer() | nil, integer()) :: t()

subagent_finished(conv, turn, child_conversation_id, status, summary)

@spec subagent_finished(
  integer(),
  integer() | nil,
  integer(),
  String.t(),
  String.t() | nil
) :: t()

subagent_started(conv, turn, child_conversation_id, role, task)

@spec subagent_started(integer(), integer() | nil, integer(), String.t(), String.t()) ::
  t()

text_delta(conv, turn, index, delta)

@spec text_delta(integer(), integer() | nil, integer(), String.t()) :: t()

thinking_delta(conv, turn, index, delta)

@spec thinking_delta(integer(), integer() | nil, integer(), String.t()) :: t()

to_wire(ev)

@spec to_wire(t()) :: map()

The wire map of an event: its payload at the top level, plus the envelope keys.

tool_blocked(conv, turn, tool_call_id, name, reason)

@spec tool_blocked(integer(), integer() | nil, String.t(), String.t(), String.t()) ::
  t()

tool_completed(conv, turn, tool_call_id, name, is_error, output)

@spec tool_completed(
  integer(),
  integer() | nil,
  String.t(),
  String.t(),
  boolean(),
  term()
) :: t()

tool_started(conv, turn, tool_call_id, name, arguments, overwritten_keys \\ [])

@spec tool_started(integer(), integer() | nil, String.t(), String.t(), map(), [
  String.t()
]) :: t()

toolcall_delta(conv, turn, index, delta, partial_args, call \\ [])

@spec toolcall_delta(
  integer(),
  integer() | nil,
  integer(),
  String.t() | nil,
  term(),
  keyword()
) :: t()

topic(conversation_id)

@spec topic(integer()) :: String.t()

PubSub topic of a conversation.

turn_cancelled(conv, turn, seq)

@spec turn_cancelled(integer(), integer() | nil, integer()) :: t()

turn_completed(conv, turn, result, seq, stop_reason \\ "end_turn")

turn_error(conv, turn, error, seq)

@spec turn_error(integer(), integer() | nil, term(), integer()) :: t()

turn_started(conv, turn, seq)

@spec turn_started(integer(), integer() | nil, integer()) :: t()

types()

@spec types() :: [String.t()]

The event types.

ui_request(conv, request_id, request)

@spec ui_request(integer(), String.t(), map()) :: t()

ui_resolved(conv, request_id, outcome)

@spec ui_resolved(integer(), String.t(), term()) :: t()

usage_updated(conv, turn, totals)

@spec usage_updated(integer(), integer() | nil, map()) :: t()

watch_started(conv, seq)

@spec watch_started(integer(), integer()) :: t()