04 — Parallel fan-out
parallel() sends the same prompt to multiple agents at the same time and collects the results. Use it standalone for Promise.allSettled-style output, or drop it directly into a workflow() step list to merge the fulfilled results for a downstream synthesiser.
Parallel group. Call .run(input) to execute all agents concurrently.fulfilled is an array of MessageResponse; rejected has reason strings.parallel([...]) anywhere in the step list. Fulfilled results are text-joined (one per line) and passed to the next step.input, credentials, or other step-level options.Run it
npm run parallel
The example runs in two parts. First a standalone fan-out that logs each agent's reply independently, then a workflow that feeds the merged results into a synthesiser agent.
Standalone fan-out
Call parallel([...]).run(input) to get the raw settled results:
const fanout = await parallel([differential, redFlags, workup]).run(presentation);
// fulfilled: MessageResponse[]
fanout.fulfilled.forEach((r, i) => console.log(`#${i + 1}: ${r.text}`));
// rejected: Array of { reason: unknown }
if (fanout.rejected.length) console.log("Rejected:", fanout.rejected);
This behaves like Promise.allSettled — a single agent failure does not abort the group. You get all results regardless.
| Field | Type | Description |
|---|---|---|
fulfilled |
MessageResponse[] |
Responses from agents that completed successfully. Order matches the input array (minus rejected entries). |
rejected |
{ reason: unknown }[] |
Failure details for agents that threw or returned a non-success status. |
Inside a workflow
A Parallel group is a valid workflow step. The SDK text-joins all fulfilled results (separated by newlines) and passes the combined string as input to the next step. Rejected results are silently discarded.
const { output } = await workflow([
parallel([differential, redFlags, workup]), // fan-out step
synthesizer, // receives joined text
]).run(presentation);
This is the most common pattern — fan out to several specialist agents, then use one more agent to synthesise their outputs into a cohesive result.
Building the agents concurrently
Since the four agents are independent, they can be created in parallel too — using plain Promise.all at the TypeScript level, not the SDK's parallel():
const [differential, redFlags, workup, synthesizer] = await Promise.all([
agents.create({ name: "p-differential", systemPrompt: "List the top 3 differentials..." }),
agents.create({ name: "p-redflags", systemPrompt: "List red-flag features..." }),
agents.create({ name: "p-workup", systemPrompt: "Suggest initial workup..." }),
agents.create({ name: "p-synthesizer", systemPrompt: "Combine the three perspectives..." }),
]);
This cuts agent registration latency from 4× to ~1×. Use this pattern whenever agents don't depend on each other's IDs.
Per-step overrides
When branches need different inputs or credentials, use the expanded step format inside parallel():
parallel([
{ agent: a, input: "Analyse from a cardiology perspective." },
{ agent: b, credentials: { "mcp-x": { type: "token", token: "..." } } },
c, // bare handle — uses the shared input passed to .run()
])
| Option | Description |
|---|---|
input | Override the shared input for this branch. Accepts a string or message parts array. |
credentials | Per-branch credential store for MCP auth (see example 06). |
Full code
Source: examples/ts/04-parallel.ts
/**
* 04 — Parallel fan-out.
*
* Run multiple agents concurrently on the same input. Use `parallel()`
* standalone, or drop it directly into a `workflow()` step list to merge
* the fulfilled results into the next step.
*
* Run: `npm run parallel`
*/
import { AgentsClient, parallel, workflow } from "@corti/agent-sdk";
import { makeClient } from "./_client";
async function main() {
const agents = new AgentsClient(makeClient());
const [differential, redFlags, workup, synthesizer] = await Promise.all([
agents.create({
name: "p-differential",
description: "Lists a differential diagnosis.",
systemPrompt:
"List the top 3 differential diagnoses for the presentation, most likely first. One line each.",
}),
agents.create({
name: "p-redflags",
description: "Flags symptoms warranting urgent evaluation.",
systemPrompt:
"List any red-flag features from the presentation that warrant urgent evaluation. One line each.",
}),
agents.create({
name: "p-workup",
description: "Suggests initial diagnostic workup.",
systemPrompt:
"Suggest an initial diagnostic workup (labs, imaging, bedside exam). One line each.",
}),
agents.create({
name: "p-synthesizer",
description: "Combines clinical perspectives into a single assessment.",
systemPrompt:
"You will receive a differential, red-flag list, and suggested workup joined by newlines. Combine them into a concise clinical assessment and plan.",
}),
]);
const presentation =
"54-year-old male with 2 hours of crushing substernal chest pain, diaphoresis, and dyspnea; history of hypertension and smoking.";
// (a) Standalone: collect every result with Promise.allSettled-like output.
const fanout = await parallel([differential, redFlags, workup]).run(presentation);
console.log("— Fan-out (standalone) —");
fanout.fulfilled.forEach((r, i) => console.log(`#${i + 1}: ${r.text}`));
if (fanout.rejected.length) console.log("Rejected:", fanout.rejected);
// (b) Inside a workflow: fulfilled results are joined and fed into `synthesizer`.
const { output } = await workflow([
parallel([differential, redFlags, workup]),
synthesizer,
]).run(presentation);
console.log("\n— Assessment and plan —\n" + output.text);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});