A TurnStream in QM tracks per-run state across an agent's full reply lifecycle—from start through block publishing, tool calls, and final confirmation—and fires callbacks when the first text block closes or the reply posts to a surface. Character limits (overall maxChars and first-block cap) and a grace-period timer prevent unbounded buffering while allowing the Node.js process to exit cleanly.
The TurnStream interface in src/runs/turn-stream.ts tracks per-run streaming state across the full lifecycle of an agent reply: from begin() through publish(), publishBlockStart(), and noteToolCall(), to markReplyDone() and end().[1] The TurnStreamListener interface in the same file exposes two optional callbacks: onFirstBlock(text), fired when the first text block is closed by a tool call, and onSurfacePosted(), fired when the reply is confirmed posted to a surface.[1]
createTurnStream() is the factory for a TurnStream instance; it accepts optional maxChars (default 200,000) and graceMs (default 30,000 ms) parameters.[1] The first text block in a run is additionally capped at 20,000 characters (FIRST_BLOCK_MAX_CHARS), independently of the overall maxChars limit.[1] The grace-period timer calls timer.unref?.() so it does not prevent the Node.js process from exiting when a run-map entry is the only remaining work.[1] The grace-period timer starts when a run ends; if the TurnStream entry is not explicitly cleared within the grace window, it is removed automatically to prevent memory leaks from replies that never receive final confirmation.
Sources