Multi-agent composition

July 29, 2026 · View on GitHub

Set agents: [...] and a strategy. Strategies: 'sequential', 'parallel', 'handoff', 'router', 'round_robin', 'random', 'swarm', 'manual', 'plan_execute'. 'plan_execute' runs a planner sub-agent that compiles a typed, inspectable Plan into a durable sub-workflow instead of delegating turn-by-turn — see deploy/serve/run/plan for the planner/fallback setup and the Plan/Step/Op builders.

// Sequential — agents run in order. .pipe() is sugar for strategy: 'sequential'.
const pipeline = writer.pipe(editor);
// equivalent to:
// new Agent({ name: 'writer_editor', agents: [writer, editor], strategy: 'sequential' });

// Parallel — agents run concurrently, results gathered
const team = new Agent({ name: 'research_team', agents: [webResearcher, dataAnalyst], strategy: 'parallel' });

// Handoff — the parent LLM delegates to sub-agents (they appear as callable tools)
const support = new Agent({
  name: 'support',
  model,
  instructions: 'Route to the right specialist.',
  agents: [billingAgent, technicalAgent, salesAgent],
  strategy: 'handoff',
});

// Router — a router agent (or function) picks the sub-agent
const routed = new Agent({
  name: 'router',
  agents: [a, b],
  strategy: 'router',
  router: routerAgent,   // an Agent or (…) => string returning a sub-agent name
});

scatterGather({ name, workers, ... }) is a convenience builder that returns a coordinator agent which fans a problem out to worker agents in parallel and synthesizes the results:

import { scatterGather } from '@io-orkes/conductor-javascript/agents';
const coordinator = scatterGather({ name: 'fanout', workers: [worker], retryCount: 2 });

Handoffs

For swarm/handoff strategies you can declare explicit handoff transitions with handoffs: [...]. Each condition has a target (a sub-agent name).

import { OnTextMention, OnToolResult, OnCondition } from '@io-orkes/conductor-javascript/agents';

const team = new Agent({
  name: 'coding_team',
  model,
  agents: [pythonExpert, jsExpert],
  strategy: 'swarm',
  handoffs: [
    // Hand off when the output mentions text (case-insensitive)
    new OnTextMention({ target: 'python_expert', text: 'Python' }),

    // Hand off when a specific tool returns (optionally only if result contains text)
    new OnToolResult({ target: 'escalation', toolName: 'detect_severity', resultContains: 'critical' }),

    // Hand off when a custom predicate returns true (runs as a worker task)
    new OnCondition({ target: 'fallback', condition: (ctx) => ctx.result.length > 1000 }),
  ],
});

You can also constrain which transitions are allowed with allowedTransitions: { agentName: ['otherAgent', ...] }.

Expected result and failures

Every child of agents: [...] is a durable sub-workflow, visible in execution history on its own. Set a termination condition and a maxTurns limit for every open-ended design (handoff/router/swarm) — an unrestricted graph that loops has no other backstop. Use allowedTransitions to restrict which specialists a handoff/swarm can reach, so an unexpected model output can't route to an unsafe agent.

Next steps

Use termination to bound multi-agent loops (e.g. round_robin debates), stateful agents for shared per-execution state, guardrails for per-agent/per-tool validation, and the handoffs reference for the full condition-class list.