Agent.streamUntilIdle()
Added in: @mastra/core@1.28.0
streamUntilIdle() streams an agent's response and keeps the stream open until every background task dispatched during the run completes. When a task finishes, its result is written to memory and the agentic loop re-enters automatically so the LLM can react to it. The stream closes once no tasks are running and no completions are queued.
Use it when the agent dispatches background tasks (typically long-running tools or subagents) and you want a single stream that spans the initial response plus every continuation triggered by a task completion. For foreground-only runs or if you prefer to manage the continuation manually (manually prompt agent to process the result), use Agent.stream().
Usage exampleDirect link to Usage example
const stream = await agent.streamUntilIdle('Research solana for me', {
memory: { thread: 't1', resource: 'u1' },
})
for await (const chunk of stream.fullStream) {
// chunks from the initial turn AND any continuation turns triggered by
// background task completions flow through here
}
streamUntilIdle() requires both a BackgroundTaskManager and a memory backend. Without either, it falls through to a plain agent.stream() call.
ParametersDirect link to Parameters
messages:
options?:
maxIdleMs?:
memory?:
structuredOutput?:
For every other option (maxSteps, modelSettings, toolChoice, outputProcessors, onFinish, onChunk, etc.), see the Agent.stream() parameters. streamUntilIdle() forwards them to the initial turn.
ReturnsDirect link to Returns
stream:
Aggregate properties caveatDirect link to Aggregate properties caveat
streamUntilIdle() returns a proxy over the first turn's MastraModelOutput. Only fullStream is replaced with a combined stream that spans every continuation. Every other property — text, toolCalls, toolResults, finishReason, messageList, getFullOutput() — resolves against the first turn's internal buffer.
If you need an aggregate view across all continuations, consume fullStream yourself and accumulate.
Continuation behaviorDirect link to Continuation behavior
Internally, streamUntilIdle():
- Runs the initial turn via
agent.stream(...)and pipes itsfullStreaminto the outer stream. - Subscribes to background-task completion events for the resolved memory scope.
- Queues each terminal event (
background-task-completed,background-task-failed,background-task-cancelled) and, when the outer wrapper is idle between turns, re-invokesagent.stream([], ...)with a directive listing the just-completedtoolCallIds. The continuation turn flows into the same outer stream. - Closes the outer stream once no tasks are running and no completions are queued.
Extended usage exampleDirect link to Extended usage example
Cap idle time between turnsDirect link to Cap idle time between turns
const stream = await agent.streamUntilIdle('Kick off the long jobs', {
memory: { thread: 't1', resource: 'u1' },
maxIdleMs: 60_000, // close the stream after 1 minute of idleness between turns
})
for await (const chunk of stream.fullStream) {
if (chunk.type === 'background-task-completed') {
console.log('Task complete:', chunk.payload.taskId)
}
}
Aggregate text across continuationsDirect link to Aggregate text across continuations
const stream = await agent.streamUntilIdle('Research and summarize', {
memory: { thread: 't1', resource: 'u1' },
})
let fullText = ''
for await (const chunk of stream.fullStream) {
if (chunk.type === 'text-delta') {
fullText += chunk.payload.text
}
}