Streaming Semantics
agency().stream(...) exposes multiple streaming surfaces because "what is
happening right now" and "what is finally approved" are not always the same
thing.
Use the right one for the job:
textStream- Raw text chunks from the strategy, as it produces them.
- Lowest latency.
- May differ from the final approved answer if output guardrails or HITL rewrite the result.
fullStream- Structured event stream.
- Includes raw text/tool/lifecycle events as they happen.
- Also includes late post-processing events such as:
approval-requestedapproval-decidedfinal-output- final agency-level
agent-end
text- Finalized scalar text after output guardrails, usage normalization, and
beforeReturnHITL approval. See Human-in-the-Loop for the full HITL surface.
- Finalized scalar text after output guardrails, usage normalization, and
finalTextStream- Finalized-text iterable.
- Yields the post-processing-approved text as one chunk once the run is finalized, and nothing when that text is empty.
usage- Finalized aggregate usage for the streamed run.
agentCalls- Finalized per-agent ledger for the streamed run.
parsed- Final structured payload when
outputis configured, parsed from the finalizedtext: after abeforeReturnapproval that replaces the text, from the replacement, andundefinedwhen that text does not match the schema. A stream runs no validation retry: wheregenerate()repeats a run whose reply does not match,stream()finalizes the one run it streamed.
- Final structured payload when
Recommended Usage
Fast chat UI
Use textStream when you want the lowest-latency token display:
const stream = team.stream('Draft the answer');
for await (const chunk of stream.textStream) {
process.stdout.write(chunk);
}
This is the right default for conversational UX, but remember it is raw output.
Approved-only UI
Use finalTextStream when the client should only ever see the approved final
answer:
const stream = team.stream('Draft the answer');
for await (const chunk of stream.finalTextStream) {
process.stdout.write(chunk);
}
It yields once, after post-processing finishes, so it waits for the whole run and never shows text that a guardrail or reviewer later changes.
Audits, orchestration visualizers, and runtime tooling
Use fullStream when you need the lifecycle:
const stream = team.stream('Draft the answer');
for await (const part of stream.fullStream) {
switch (part.type) {
case 'text':
console.log('raw text', part.text);
break;
case 'approval-requested':
console.log('approval requested', part.request.id);
break;
case 'approval-decided':
console.log('approval decided', part.approved);
break;
case 'final-output':
console.log('final text', part.text);
console.log('usage', part.usage.totalTokens);
console.log('agentCalls', part.agentCalls.length);
break;
}
}
Important Distinction
If you enable output guardrails or hitl.approvals.beforeReturn, the final
approved answer can differ from the raw streamed text.
That means:
textStreamcan show content that is later rewritten.textandfinalTextStreamare the truthful finalized answer.fullStreamis the only stream that shows both the raw path and the final approval/finalization events in one place.
Which Strategies Stream Token by Token
sequentialandgraphstream each agent's tokens as they arrive.fullStreambrackets every agent withagent-startandagent-endparts and tags eachtextpart with itsagent.graphruns the agents of a tier one after another while it streams, wheregenerate()runs them in parallel.parallel,debate,review-loopandhierarchicalrun to completion first.textStreamthen yields the whole text as one chunk, andfullStreamcarries that onetextpart before the post-processing parts. An agency created withadaptive: trueruns any other strategy under a hierarchical manager, so it streams this way too.
In sequential and graph, textStream carries the text of every agent in
turn, not only the last one, and the streamed text joins all of it in
order. generate() returns the last agent's text instead (for graph, the
last agent of the final tier).
Guarantees
final-outputis emitted intofullStreamafter post-processing completes.sequentialandgraphend their raw stream with anagent-endpart for__agency__(sequentialwith the last agent's text asoutput,graphwith an emptyoutput), so that part comes beforefinal-output.- After
final-output,fullStreamadds anagent-endfor the agency (itsname, or__agency__) with the final text asoutput, unless an earlier part already carried that agent and that output. text,usage,agentCallsandparsedresolve once the run is finalized.textStreamcarries the text before output guardrails and beforebeforeReturnapproval. Output guardrails act on the finalized text only, so no stream carries guardrail-rewritten tokens while the run streams.
Practical Rule
Use this rule unless you have a reason not to:
textStreamfor speedfinalTextStreamfor correctnessfullStreamfor tooling, observability, and auditstextfor the simplest finalized scalar result