Maniac Docs
Streaming & tracing

Correlation IDs

turn_id, message_id, thread_id, block_id, and span fields on TraceEvent for UI routing and nested runs.

Correlation IDs

Every TraceEvent carries a standard envelope plus optional correlation fields. Use them to route streaming tokens to the right chat bubble, group tool events under the LM iteration that spawned them, and scope memory writes to a conversation thread.

Standard envelope

Assigned by Tracer.emit (callers should not pre-populate these — the tracer overwrites them):

FieldDescription
run_idStable id for the entire run (tracer constructor or OTelTracer option)
event_idUUID per event
seqMonotonic counter starting at 0
span_id / parent_span_idNested span tree for sub-agents and LM iterations
depthNesting depth (0 = root agent)
principalAgent id that produced the event
tsISO-8601 timestamp

Per-iteration IDs

The runner mints correlation ids at the top of each LM iteration and stores them on RunContext so downstream emissions inherit stable linkage:

FieldScopeDescription
turn_idPer LM iterationUUID shared by lm_call_start, token, lm_call, tool, step, and other events in the same loop iteration. Reuses the LM span id.
message_idPer assistant messageUUID for the assistant message produced by the iteration. Tokens and the closing lm_call share this id.
block_idPer token stream segmentUUID per contiguous run of the same chunk_kind on token events (text vs reasoning vs tool-call deltas).
thread_idPer conversationCaller-scoped thread from RunOptions.threadId / Maniac.chat({ threadId }). Propagated through nested runs and stamped on memory events.

All four optional fields are nullable — consumers that do not need routing can ignore them.

import { runAgentStream } from "@maniac-ai/agents";

const byMessage = new Map<string, string>();

for await (const env of runAgentStream(spec, "Hello", { threadId: "support-42" })) {
  if (env.type !== "event" || env.event.kind !== "token") continue;

  const { message_id, turn_id, thread_id, delta } = env.event;
  if (message_id) {
    byMessage.set(message_id, (byMessage.get(message_id) ?? "") + (delta ?? ""));
  }
}

thread_id propagation

Set threadId on runAgent / runAgentStream / Maniac.chat options. Nested sub-agent runs inherit the parent's thread_id. Background tasks default to bg:<taskId> when no thread is supplied.

Memory stores (ConversationStore, ObservationStore, WorkingMemoryStore) namespace records by (agent_id, thread_id) under thread scope.

Nested runs

Sub-agent and parallel fan-out events carry depth > 0, their own span_id, and parent_span_id pointing at the enclosing agent span. principal identifies the inner agent id; turn_id still reflects the root iteration that triggered the nested call when emitted from within that iteration's tool handler.

Tools that call getActiveRunContext() read the active turn_id, message_id, and thread_id for custom trace emissions.

JSON Schema

The full TraceEvent shape is published at @maniac-ai/agents/trace-event.schema.json (also in dist/ after build) for Python validators, fixture generators, and IDE tooling.

OTel mapping

OTelTracer reuses maniac span_id / parent_span_id for OpenTelemetry parent context. See OTelTracer.

On this page