Stately
PackagesAgent

Observability

Use XState inspection for actor/runtime behavior and Agent traces for model-request behavior.

Trace one run

await runAgent(machine, {
  input,
  executors,
  inspect: (event) => xstateInspector.next(event),
  onTrace: (event) => exporter.write(serializeTraceEvent(event))
});

Agent trace kinds are run.start, request.start, request.end, request.error, stream.chunk, machine.transition, emit, usage.dropped, and run.end.

A request.end event carries the call's output and raw, plus what the executor reported about the call itself: usage when it reported tokens, and finishReason when it reported why the call stopped, normalized to 'stop', 'length', 'tool-calls', 'content-filter', or 'other'. See Finish reason and truncation.

serializeTraceEvent creates a JSON-safe projection. Traces are observations, not a persistence protocol; the event log is.

usage.dropped means the @agent.usage event was not delivered to the machine, because no active state accepted it or the leg had already settled. The spend is still journaled and still counted: the entry is in the event log, and getUsageFromEvents folds it into the totals. Only the machine event is dropped.

Async stream

for await (const event of runAgentStream(machine, { input, executors })) {
  if (event.kind === "chunk") process.stdout.write(event.delta);
  if (event.kind === "transition") renderState(event.value);
}

The terminal kind is done, idle, or error. There is no Agent-specific failure status; domain failure is represented by the machine's typed final output.

State paths in logs

getStatePath(snapshot) renders a state value as one deterministic string, so a log line or a progress field survives nesting and parallel regions. String(snapshot.value) renders every non-atomic value as [object Object].

onTransition: (snapshot) => log.info({ state: getStatePath(snapshot) });

An atomic state renders as writing, a nested one as review.editing.draft, and a parallel one as p:{left.x,right.a.b} with its regions sorted by name.

OpenTelemetry

Use createOtelTraceHandler from @statelyai/agent/otel as an onTrace sink. Framework telemetry and XState inspection remain composable with it.

On this page