Domain-typed event envelope — the vocabulary root for everything that
travels the llm.* / agent.* / workflow.* streams. domain/type are
closed atom unions, usage/tool are the promoted commons consumers
actually query, and anything provider-specific rides raw (the documented
verbatim carve-out).
Build with llm/2, agent/2, or workflow/2 — each validates type
against its domain's closed union and returns {:ok, t()} | {:error, {:bad_type, domain, type}}. Never construct the struct directly.
to_wire/1 / from_wire/1 are the struct-in-BEAM / JSON-at-the-boundary
pair (the house DecisionRecord.to_event pattern): to_wire/1 always
succeeds and produces a string-keyed map with nil ids omitted and nil
usage/tool serialized as absent keys, never null. from_wire/1 is the
fallible, tolerant inverse — unknown top-level keys are ignored, an unknown
"type" for the domain is {:error, {:bad_event, {:bad_type, ...}}}, and
anything else unparseable is {:error, {:bad_event, reason}}. It never
raises and never calls String.to_atom/1 — types are resolved through
fixed lookup maps built from the closed unions.
Type unions are closed per release; tolerance for unknown telemetry frames lives at collection (collectors count + debug-log + drop), never here.
Summary
Functions
Build an agent.* event. See llm/2 for attrs.
Parse a wire-form map back into a t(). Fallible and tolerant: unknown
top-level keys are ignored, a non-map or a map missing "domain"/"type"
is {:error, {:bad_event, {:invalid_wire, term()}}}, an unrecognized
"domain" is {:error, {:bad_event, {:bad_domain, term()}}}, and an
unrecognized "type" for the domain is {:error, {:bad_event, {:bad_type, domain, term()}}}. Never raises; never calls String.to_atom/1.
Build an llm.* event. type must be one of llm_type/0; attrs is a
keyword list or map carrying :seq (default 0), :ts (default
System.monotonic_time(:nanosecond)), the four correlation ids, :usage,
:tool, and :raw (default %{}).
Render a t() to its wire form: a string-keyed map, correlation ids nested
under "ids" (nils omitted, "ids" itself always present), "usage"/
"tool" present only when not nil, "raw" always present.
Build a workflow.* event. See llm/2 for attrs.
Types
@type agent_type() ::
:session_open
| :session_reattach
| :turn_start
| :turn_end
| :terminal
| :error
@type domain() :: :llm | :agent | :workflow
@type llm_type() ::
:request_start
| :request_stop
| :reasoning
| :tool_call
| :usage
| :turn_complete
| :exception
@type t() :: %Mimir.Event{ domain: domain(), raw: map(), request_id: String.t() | nil, seq: non_neg_integer(), session_id: String.t() | nil, step_id: String.t() | nil, tool: %{id: String.t() | nil, name: String.t()} | nil, ts: integer(), type: llm_type() | agent_type() | workflow_type(), usage: %{input_tokens: non_neg_integer(), output_tokens: non_neg_integer()} | nil, workflow_id: String.t() | nil }
@type workflow_type() :: :step_start | :step_stop | :step_exception
Functions
@spec agent(agent_type(), keyword() | map()) :: {:ok, t()} | {:error, {:bad_type, :agent, term()}}
Build an agent.* event. See llm/2 for attrs.
Parse a wire-form map back into a t(). Fallible and tolerant: unknown
top-level keys are ignored, a non-map or a map missing "domain"/"type"
is {:error, {:bad_event, {:invalid_wire, term()}}}, an unrecognized
"domain" is {:error, {:bad_event, {:bad_domain, term()}}}, and an
unrecognized "type" for the domain is {:error, {:bad_event, {:bad_type, domain, term()}}}. Never raises; never calls String.to_atom/1.
Build an llm.* event. type must be one of llm_type/0; attrs is a
keyword list or map carrying :seq (default 0), :ts (default
System.monotonic_time(:nanosecond)), the four correlation ids, :usage,
:tool, and :raw (default %{}).
Render a t() to its wire form: a string-keyed map, correlation ids nested
under "ids" (nils omitted, "ids" itself always present), "usage"/
"tool" present only when not nil, "raw" always present.
@spec workflow(workflow_type(), keyword() | map()) :: {:ok, t()} | {:error, {:bad_type, :workflow, term()}}
Build a workflow.* event. See llm/2 for attrs.