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 →
errortrace event, erroredAgentResult - Consumer early exit →
RunCancelledErrorpath 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.