Maniac Docs
Streaming & tracing

Stream envelopes

StreamEnvelope tagged union for runAgentStream, paused HITL checkpoints, and cancellation via AbortSignal.

Stream envelopes

runAgentStream, runAgentResumeStream, and Maniac.chatStream yield a tagged StreamEnvelope<T> instead of a raw TraceEvent | AgentResult union. Switch on env.type to route events, pause checkpoints, and the terminal result.

type StreamEnvelope<T = string> =
  | { type: "event"; event: TraceEvent }
  | { type: "paused"; checkpoint: RunCheckpoint; pending: PendingApproval[] }
  | { type: "result"; result: AgentResult<T> };

The legacy alias StreamItem<T> = TraceEvent | AgentResult<T> is deprecated — new code should use StreamEnvelope.

Event envelopes

Every trace event emitted during the run is wrapped as { type: "event", event } in seq order. Token deltas, tool completions, guardrail blocks, and nested sub-agent boundaries all arrive on the same iterator.

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

for await (const env of runAgentStream(spec, "Summarize this repo")) {
  if (env.type !== "event") continue;

  switch (env.event.kind) {
    case "token":
      if (env.event.chunk_kind === "text") {
        process.stdout.write(env.event.delta ?? "");
      }
      break;
    case "tool":
      console.error("tool:", env.event.payload);
      break;
    case "guardrail":
      console.error("blocked:", env.event.payload);
      break;
  }
}

Enable LM-level token streaming inside the runner with streamLmCalls: true (set automatically by runAgentStream). See Inference streaming for model-layer StreamChunk kinds.

Paused envelopes

When a run hits a human-in-the-loop checkpoint (status: "paused"), the stream yields a { type: "paused" } envelope before the terminal { type: "result" }. Consumers that only handle "event" and "result" keep working; UIs that want to surface approvals early can react to "paused" without unwrapping AgentResult.

for await (const env of runAgentStream(spec, query)) {
  if (env.type === "paused") {
    renderApprovalCard(env.pending, env.checkpoint);
  }
  if (env.type === "result" && env.result.status === "paused") {
    // terminal envelope still emitted — resume with runAgentResumeStream
  }
}

See HITL checkpoints for resume flow.

Result envelope

Exactly one { type: "result", result } terminates a successful stream iteration. result.trace duplicates the full event buffer; result.messages carries the persisted transcript delta.

Maniac.chatStream uses the same envelope shape and additionally persists turns to ConversationStore when the result completes (including partial transcripts on errored or interrupted runs).

Cancellation

Pass RunOptions.signal: AbortSignal to cooperative cancellation. If the consumer breaks the for await loop early (break, return on the iterator), the runner aborts the inner controller so in-flight model calls and tools see cancellation.

  • Abort via signal → error trace event, errored AgentResult
  • Consumer early exit → RunCancelledError path with partial transcript when available

runAgentResumeStream follows the same contract for resumed runs.

Background task streaming

BackgroundTaskDispatcher merges child task tracers into the parent dispatcher tracer. Maniac.runStreamUntilIdle yields envelopes from the foreground run plus every in-flight background child — see Background tasks.

Channel adapters

toChatStream in @maniac-ai/agents/channels maps StreamEnvelope events into Chat-SDK UI message parts for Slack and webhook bots.

On this page