Skip to main content

Agent networks

Deprecated — Use supervisor agents

Agent networks are deprecated and will be removed in a future major release. Supervisor agents using agent.stream() or agent.generate() are now the recommended approach. It provides the same multi-agent coordination with better control, a simpler API, and easier debugging.

See the migration guide to upgrade.

A routing agent uses an LLM to interpret a request and decide which primitives (subagents, workflows, or tools) to call, in what order, and with what data.

Create an agent network
Direct link to Create an agent network

Configure a routing agent with agents, workflows, and tools. Memory is required as .network() uses it to store task history and determine when a task is complete.

Each primitive needs a clear description so the routing agent can decide which to use. For workflows and tools, inputSchema and outputSchema also help the router determine the right inputs.

src/mastra/agents/routing-agent.ts
import { Agent } from '@mastra/core/agent'
import { Memory } from '@mastra/memory'
import { LibSQLStore } from '@mastra/libsql'

import { researchAgent } from './research-agent'
import { writingAgent } from './writing-agent'
import { cityWorkflow } from '../workflows/city-workflow'
import { weatherTool } from '../tools/weather-tool'

export const routingAgent = new Agent({
id: 'routing-agent',
name: 'Routing Agent',
instructions: `
You are a network of writers and researchers. The user will ask you to research a topic. Always respond with a complete report—no bullet points. Write in full paragraphs, like a blog post. Do not answer with incomplete or uncertain information.`,
model: 'openai/gpt-5.4',
agents: {
researchAgent,
writingAgent,
},
workflows: {
cityWorkflow,
},
tools: {
weatherTool,
},
memory: new Memory({
storage: new LibSQLStore({
id: 'mastra-storage',
url: 'file:../mastra.db',
}),
}),
})
note

Subagents need a description on the Agent instance. Workflows and tools need a description plus inputSchema and outputSchema on createWorkflow() or createTool().

Call the network
Direct link to Call the network

Call .network() with a user message. The method returns a stream of events you can iterate over.

const result = await routingAgent.network('Tell me three cool ways to use Mastra')

for await (const chunk of result) {
console.log(chunk.type)
if (chunk.type === 'network-execution-event-step-finish') {
console.log(chunk.payload.result)
}
}

Structured output
Direct link to Structured output

Pass structuredOutput to get typed, validated results. Use objectStream for partial objects as they generate.

import { z } from 'zod'

const resultSchema = z.object({
summary: z.string().describe('A brief summary of the findings'),
recommendations: z.array(z.string()).describe('List of recommendations'),
confidence: z.number().min(0).max(1).describe('Confidence score'),
})

const stream = await routingAgent.network('Research AI trends', {
structuredOutput: { schema: resultSchema },
})

for await (const partial of stream.objectStream) {
console.log('Building result:', partial)
}

const final = await stream.object
console.log(final?.summary)

Approve and decline tool calls
Direct link to Approve and decline tool calls

When a primitive requires approval, the stream emits an agent-execution-approval or tool-execution-approval chunk. Use approveNetworkToolCall() or declineNetworkToolCall() to respond.

Network approval uses snapshots to capture execution state. Ensure a storage provider is enabled in your Mastra instance.

src/approve-network.ts
const stream = await routingAgent.network('Perform some sensitive action', {
memory: {
thread: 'user-123',
resource: 'my-app',
},
})

for await (const chunk of stream) {
if (chunk.type === 'agent-execution-approval' || chunk.type === 'tool-execution-approval') {
// Approve
const approvedStream = await routingAgent.approveNetworkToolCall(chunk.payload.toolCallId, {
runId: stream.runId,
memory: { thread: 'user-123', resource: 'my-app' },
})

for await (const c of approvedStream) {
if (c.type === 'network-execution-event-step-finish') {
console.log(c.payload.result)
}
}
}
}

To decline instead, call declineNetworkToolCall() with the same arguments.

Suspend and resume
Direct link to Suspend and resume

When a primitive calls suspend(), the stream emits a suspension chunk (e.g., tool-execution-suspended). Use resumeNetwork() to provide the requested data and continue execution.

src/resume-network.ts
const stream = await routingAgent.network('Delete the old records', {
memory: { thread: 'user-123', resource: 'my-app' },
})

for await (const chunk of stream) {
if (chunk.type === 'workflow-execution-suspended') {
console.log(chunk.payload.suspendPayload)
}
}

// Resume with user confirmation
const resumedStream = await routingAgent.resumeNetwork(
{ confirmed: true },
{
runId: stream.runId,
memory: { thread: 'user-123', resource: 'my-app' },
},
)

for await (const chunk of resumedStream) {
if (chunk.type === 'network-execution-event-step-finish') {
console.log(chunk.payload.result)
}
}

Automatic resumption
Direct link to Automatic resumption

Set autoResumeSuspendedTools to true so the network resumes suspended primitives based on the user's next message. This creates a conversational flow where users provide the required information naturally.

const stream = await routingAgent.network('Delete the old records', {
autoResumeSuspendedTools: true,
memory: { thread: 'user-123', resource: 'my-app' },
})

Requirements for automatic resumption:

  • Memory configured: The agent needs memory to track suspended tools across messages.
  • Same thread: The follow-up message must use the same thread and resource identifiers.
  • resumeSchema defined: The tool must define a resumeSchema so the network can extract data from the user's message.
Manual (resumeNetwork)Automatic (autoResumeSuspendedTools)
Best forCustom UIs with approval buttonsChat-style interfaces
ControlFull control over resume timing and dataNetwork extracts data from user's message
SetupHandle suspension chunks, call resumeNetworkSet flag, define resumeSchema on tools