Multi-agent patterns.
Compose, parallelise, and route between agents.
The SDK has no built-in orchestrator — multi-agent emerges from composition. The Runner is your execution primitive, and standard JavaScript patterns (chaining, Promise.all, tool-based delegation) build the architecture.
Pattern 1: Sequential pipeline
Chain run() calls, feeding each agent's output to the next. Each stage has a single responsibility.
import { Agent, Runner } from '@maincode-ai/matilda-agent-sdk';
const runner = new Runner();
const researcher = new Agent({
name: 'researcher',
purpose: 'analysis',
instructions: 'Produce a structured list of key facts for a blog post. Bullet points only.',
});
const writer = new Agent({
name: 'writer',
purpose: 'general',
instructions: 'Given research notes, write an engaging blog post draft under 400 words.',
});
const editor = new Agent({
name: 'editor',
purpose: 'general',
instructions: 'Polish the draft for clarity, grammar, and flow. Return the full revised post.',
});
const topic = 'Why developers are adopting AI coding assistants';
// Stage 1 → 2 → 3
const research = await runner.run(researcher, `Research this topic: ${topic}`);
const draft = await runner.run(writer, research.finalOutput);
const edited = await runner.run(editor, draft.finalOutput);
console.log(edited.finalOutput);
// Total token usage across the pipeline
const totalTokens =
(research.usage?.output_tokens ?? 0) +
(draft.usage?.output_tokens ?? 0) +
(edited.usage?.output_tokens ?? 0);
console.log(`Total output tokens: ${totalTokens}`);Pattern 2: Parallel fan-out / fan-in
Run multiple specialist agents concurrently with Promise.all(), then feed their outputs to a synthesiser.
const securityReviewer = new Agent({
name: 'security-reviewer',
purpose: 'analysis',
instructions: 'Review code for vulnerabilities. Report only security issues.',
});
const performanceReviewer = new Agent({
name: 'performance-reviewer',
purpose: 'analysis',
instructions: 'Review code for efficiency. Report only performance issues.',
});
const synthesiser = new Agent({
name: 'synthesiser',
purpose: 'analysis',
instructions: 'Given reviews from multiple reviewers, produce a prioritised action list.',
});
const code = 'function getUserData(userId, db) { /* ... */ }';
// Fan-out: three reviewers analyse concurrently
const [security, performance] = await Promise.all([
runner.run(securityReviewer, `Review this code:\n\`\`\`javascript\n${code}\n\`\`\``),
runner.run(performanceReviewer, `Review this code:\n\`\`\`javascript\n${code}\n\`\`\``),
]);
// Fan-in: synthesiser merges the reviews
const combinedInput = [
'## Security Review', security.finalOutput,
'## Performance Review', performance.finalOutput,
].join('\n');
const synthesis = await runner.run(synthesiser, combinedInput);
console.log(synthesis.finalOutput);Pattern 3: Router / delegator
A triage agent receives queries and decides which specialist to invoke. Each specialist is exposed as a client tool — when the agent calls a tool, the SDK handler runs the specialist agent via run() and returns its output.
import { Agent, Runner, type ToolHandlers, type AgentRunOptions } from '@maincode-ai/matilda-agent-sdk';
const runner = new Runner();
const billingSpecialist = new Agent({
name: 'billing-specialist',
instructions: 'You are a billing support specialist.',
});
const technicalSpecialist = new Agent({
name: 'technical-specialist',
instructions: 'You are a technical support specialist. Include code examples when relevant.',
});
const triageAgent = new Agent({
name: 'triage',
purpose: 'general',
instructions: 'You are a customer support triage specialist.',
});
const triageTools = [
{
name: 'ask_billing_specialist',
description: 'Route billing questions to the billing specialist.',
parameters: { type: 'object' as const, properties: { question: { type: 'string' } }, required: ['question'] },
},
{
name: 'ask_technical_specialist',
description: 'Route technical questions to the technical specialist.',
parameters: { type: 'object' as const, properties: { question: { type: 'string' } }, required: ['question'] },
},
];
const toolHandlers: ToolHandlers = {
ask_billing_specialist: async (args) => {
const result = await runner.run(billingSpecialist, `Answer: ${args.question}`);
return { content: result.finalOutput };
},
ask_technical_specialist: async (args) => {
const result = await runner.run(technicalSpecialist, `Answer: ${args.question}`);
return { content: result.finalOutput };
},
};
const runOptions: AgentRunOptions = {
toolHandlers,
clientTools: triageTools,
maxToolRoundtrips: 6,
callbacks: {
onToolCall: (name) => console.log(`Triage chose: ${name}`),
onToolResult: (_name, result) => console.log(`Specialist responded.`),
onToken: (delta) => process.stdout.write(delta),
},
};
const triagePrompt = [
'A customer asked:',
'"I\'m getting a 401 Unauthorized error when calling the /api/chat endpoint."',
'You MUST forward this to a specialist by calling a tool.',
'After the specialist responds, relay their answer.',
].join('\n');
await runner.run(triageAgent, triagePrompt, runOptions);Tip: Keep the triage agent's instructions short — put the routing rules in the task prompt. If routing rules are in the instructions field, the server's Auto-mode system prompt may interpret them as a prompt-injection attempt rather than operating instructions.