Pixir.Event (pixir v0.1.0)

Copy Markdown View Source

The one Event type (ADR 0004): a plain tagged map with a common envelope

%{id: id, session_id: session_id, seq: seq, ts: ts, type: type, data: data}

built exclusively through the constructors here. The same shape serves live display (over Pixir.Events) and durable replay (the NDJSON Pixir.Log).

Key conventions (round-trip rule)

The envelope keys (:id, :session_id, :seq, :ts, :type, :data) are always atoms, and :type is always an atom from the known vocabulary. The :data payload uses string keys — the JSON wire form — so that an Event survives a Log round-trip (append then fold) structurally unchanged. Every consumer (the Provider folding History, the Renderer displaying live or replayed events) sees the same shape whether the Event is fresh or reloaded.

Canonical vs ephemeral

  • Canonical events — :user_message, :assistant_message, :reasoning, :skill_activation, :subagent_event, :session_fork, :branch_summary, :history_compaction, :provider_usage, :tool_call, :tool_result, :permission_decision — are stamped with a per-Session monotonic seq, appended to the Log, and define History (a fold over them).
  • Ephemeral events — :text_delta, :reasoning_delta, :status — are broadcast for live display and never persisted (seq stays nil).

Streaming partials are always ephemeral; only the final :assistant_message is canonical. This avoids the partial-vs-final duplication noted in ADR 0004.

Summary

Functions

Final assistant answer for a Turn (the persisted form of streamed text).

A lossy synthesis of context carried into a Fork (ADR 0024). data includes summary, source range, strategy, and explicit limitations. Distinct from history_compaction.

True if the Event's type is canonical (must be logged).

The set of canonical (persisted) event types.

A context-pressure notice (ADR 0020): the advisory/warning/recovery output for the human channel. data is string-keyed ("tier", gauge evidence, optional "next_actions" / "message"). Ephemeral by construction — like :status, it never enters the Log, so it can never reach Provider input or any replay path.

True if the Event's type is ephemeral (live-only, never logged).

The set of ephemeral (live-only) event types.

A durable History compaction checkpoint. data is a string-keyed summary with the compacted range, deterministic summary, and audit metadata used by Provider replay to keep context bounded without losing the Log as source of truth.

Build an Event. seq is left nil here; the owning Pixir.Session stamps it on canonical events when appending to the Log (see with_seq/2).

A permission decision for a gated tool call (ADR 0006).

A live to-do list (ACP plan, epic D.1). entries is the COMPLETE current plan (ACP replaces the whole list per update — never a delta); each entry is a string-keyed %{"content" => ..., "priority" => "high|medium|low", "status" => "pending|in_progress|completed"}. Ephemeral/presentation-only — like :status, a plan never enters the Log (ADR 0003); the Provider fold has no notion of it.

Durable Provider accounting for one model call. data is string-keyed evidence such as model, call index, prompt-cache key metadata, raw usage, and normalized usage summary. This is Harness observability and is never replayed as model context.

An encrypted reasoning item (rs_…) the model produced and the Responses API requires re-injected on subsequent turns (ADR 0007). item is the raw, opaque provider object (string-keyed, incl. encrypted_content); model is the model id that produced it, so replay can drop items captured under a different model.

A chunk of streamed reasoning text.

Durable fork lineage for a child Session (ADR 0024). data records the parent Session, fork-tree root, replay boundary, workspaces, and replay evidence. Provider replay treats this as lineage metadata, not conversational History.

A Skill selected for a Turn (ADR 0010). data is a string-keyed snapshot that includes the Skill identity, source/scope, resolved path, content hash, and the SKILL.md content used for that Turn.

A coarse status transition for live display (e.g. "thinking").

A parent-visible Subagent lifecycle event (ADR 0011). data carries subagent_id, child_session_id, event, status, agent, task, depth, workspace, and optional summary.

A chunk of streamed assistant text.

A tool/function the model asked to run. args is the model-supplied argument map (string keys, as decoded from JSON).

The outcome of executing a tool call. result is a string-keyed map; by convention it carries "ok" plus either "output" or a structured "error" (ADR 0005).

User input that opens a Turn.

Return the Event with its monotonic seq stamped.

Types

t()

@type t() :: %{
  id: String.t(),
  session_id: String.t(),
  seq: non_neg_integer() | nil,
  ts: String.t(),
  type: type(),
  data: map()
}

type()

@type type() :: atom()

Functions

assistant_message(session_id, text, opts \\ [])

@spec assistant_message(String.t(), String.t(), keyword()) :: t()

Final assistant answer for a Turn (the persisted form of streamed text).

branch_summary(session_id, data, opts \\ [])

@spec branch_summary(String.t(), map(), keyword()) :: t()

A lossy synthesis of context carried into a Fork (ADR 0024). data includes summary, source range, strategy, and explicit limitations. Distinct from history_compaction.

canonical?(map)

@spec canonical?(t()) :: boolean()

True if the Event's type is canonical (must be logged).

canonical_types()

@spec canonical_types() :: [type()]

The set of canonical (persisted) event types.

context_pressure(session_id, data, opts \\ [])

@spec context_pressure(String.t(), map(), keyword()) :: t()

A context-pressure notice (ADR 0020): the advisory/warning/recovery output for the human channel. data is string-keyed ("tier", gauge evidence, optional "next_actions" / "message"). Ephemeral by construction — like :status, it never enters the Log, so it can never reach Provider input or any replay path.

ephemeral?(map)

@spec ephemeral?(t()) :: boolean()

True if the Event's type is ephemeral (live-only, never logged).

ephemeral_types()

@spec ephemeral_types() :: [type()]

The set of ephemeral (live-only) event types.

history_compaction(session_id, data, opts \\ [])

@spec history_compaction(String.t(), map(), keyword()) :: t()

A durable History compaction checkpoint. data is a string-keyed summary with the compacted range, deterministic summary, and audit metadata used by Provider replay to keep context bounded without losing the Log as source of truth.

new(session_id, type, data \\ %{}, opts \\ [])

@spec new(String.t(), type(), map(), keyword()) :: t()

Build an Event. seq is left nil here; the owning Pixir.Session stamps it on canonical events when appending to the Log (see with_seq/2).

permission_decision(session_id, call_id, decision, opts \\ [])

@spec permission_decision(String.t(), String.t(), atom(), keyword()) :: t()

A permission decision for a gated tool call (ADR 0006).

plan(session_id, entries, opts \\ [])

@spec plan(String.t(), [map()], keyword()) :: t()

A live to-do list (ACP plan, epic D.1). entries is the COMPLETE current plan (ACP replaces the whole list per update — never a delta); each entry is a string-keyed %{"content" => ..., "priority" => "high|medium|low", "status" => "pending|in_progress|completed"}. Ephemeral/presentation-only — like :status, a plan never enters the Log (ADR 0003); the Provider fold has no notion of it.

provider_usage(session_id, data, opts \\ [])

@spec provider_usage(String.t(), map(), keyword()) :: t()

Durable Provider accounting for one model call. data is string-keyed evidence such as model, call index, prompt-cache key metadata, raw usage, and normalized usage summary. This is Harness observability and is never replayed as model context.

reasoning(session_id, item, model, opts \\ [])

@spec reasoning(String.t(), map(), String.t(), keyword()) :: t()

An encrypted reasoning item (rs_…) the model produced and the Responses API requires re-injected on subsequent turns (ADR 0007). item is the raw, opaque provider object (string-keyed, incl. encrypted_content); model is the model id that produced it, so replay can drop items captured under a different model.

reasoning_delta(session_id, chunk, opts \\ [])

@spec reasoning_delta(String.t(), String.t(), keyword()) :: t()

A chunk of streamed reasoning text.

session_fork(session_id, data, opts \\ [])

@spec session_fork(String.t(), map(), keyword()) :: t()

Durable fork lineage for a child Session (ADR 0024). data records the parent Session, fork-tree root, replay boundary, workspaces, and replay evidence. Provider replay treats this as lineage metadata, not conversational History.

skill_activation(session_id, data, opts \\ [])

@spec skill_activation(String.t(), map(), keyword()) :: t()

A Skill selected for a Turn (ADR 0010). data is a string-keyed snapshot that includes the Skill identity, source/scope, resolved path, content hash, and the SKILL.md content used for that Turn.

status(session_id, status, opts \\ [])

@spec status(String.t(), String.t(), keyword()) :: t()

A coarse status transition for live display (e.g. "thinking").

subagent_event(session_id, data, opts \\ [])

@spec subagent_event(String.t(), map(), keyword()) :: t()

A parent-visible Subagent lifecycle event (ADR 0011). data carries subagent_id, child_session_id, event, status, agent, task, depth, workspace, and optional summary.

text_delta(session_id, chunk, opts \\ [])

@spec text_delta(String.t(), String.t(), keyword()) :: t()

A chunk of streamed assistant text.

tool_call(session_id, call_id, name, args, opts \\ [])

@spec tool_call(String.t(), String.t(), String.t(), map(), keyword()) :: t()

A tool/function the model asked to run. args is the model-supplied argument map (string keys, as decoded from JSON).

tool_result(session_id, call_id, result, opts \\ [])

@spec tool_result(String.t(), String.t(), map(), keyword()) :: t()

The outcome of executing a tool call. result is a string-keyed map; by convention it carries "ok" plus either "output" or a structured "error" (ADR 0005).

user_message(session_id, text, opts \\ [])

@spec user_message(String.t(), String.t(), keyword()) :: t()

User input that opens a Turn.

Optional :resources is a list of Session Resource descriptors (ADR 0021). Descriptors contain local identity and hashes only; raw image bytes or base64 payloads must never be placed in the Log.

with_seq(event, seq)

@spec with_seq(t(), non_neg_integer()) :: t()

Return the Event with its monotonic seq stamped.