# Multi-agent flows

Several named agents, different CLIs and models, one flow — plus durable human approval and handoff to child flows.

Rendered page: https://agentrelay.com/docs/relayflows/multi-agent
Markdown endpoint: https://agentrelay.com/docs/relayflows/markdown/multi-agent.md

---

A flow isn't locked to one CLI or one model. Name as many agents as the task needs, give each one its own `cli` and `model`, and assign them to steps — the planning step can run on Opus, the edit can go to Codex, and the review can come back to Claude, all in one run, each step's identity and cost accounted separately.

## Named agents

```yaml
version: '0.1.0'
name: ship-feature
agents:
  planner: { cli: claude, model: claude-opus-4-6 }
  implementer: { cli: codex, model: gpt-5.6-codex }
  reviewer: { cli: claude, model: claude-sonnet-4-6 }

steps:
  - id: plan
    type: agent
    agent: planner
    instruction: 'Plan the implementation for: add OAuth2 support'

  - id: implement
    type: agent
    agent: implementer
    dependsOn: [plan]
    instruction: 'Implement the plan from the previous step.'

  - id: review
    type: agent
    agent: reviewer
    dependsOn: [implement]
    instruction: 'Review the diff for correctness and security.'
    verification:
      type: output_contains
      value: 'APPROVED'
```

Each step's `agent:` selector resolves to that named `{ cli, model }` pair at compile time, from an explicit declaration. `flows check` also flags a named agent nobody ever selects, and one that a step overrides without using, so a stale declaration doesn't quietly rot in the spec.

> The named-agent **map** (`agents:` plus a step's `agent:` selector,
> reused across steps by name) is YAML/JSON authoring only today. In
> TypeScript, `f.agent(name, options)` takes `{ task, workspace?, cli?,
> model? }` ([flows#310](https://github.com/AgentWorkforce/flows/issues/310))
> — so a call can set its own `cli`/`model` directly, but the `name`
> argument still just labels the call in the journal; there's no map to
> select a declared pair from, or to reuse across multiple calls, the
> way YAML's `agent:` selector does ([flows#300](https://github.com/AgentWorkforce/flows/issues/300)).

## Asking a human, then handing off

```ts
import { flow } from '@relayflows/surface';

export default flow('ship-feature', async (f) => {
  const plan = await f.agent('planner', {
    task: 'Plan implementation for: add OAuth2 support',
  });

  const ok = await f.human(`Ship this?\n${plan.summary}`, { to: 'khaliq' });
  if (!ok) return f.done('canceled');

  const pr = await f.dispatch('garden/implement', plan);
  f.done('success');
});
```

`f.human` parks the run on a durable wait — nothing sits there blocking a thread, and the wait survives a restart exactly like a crash mid-step does. `f.dispatch` hands the plan to a named child flow and returns its typed result, so one large flow decomposes into several smaller ones instead of a script that tries to do everything.

## Next

- [Cloud](https://agentrelay.com/docs/relayflows/cloud): Run the same flow on hosted infrastructure.
  - [Memory & integrations](https://agentrelay.com/docs/relayflows/memory-and-integrations): Recall past runs; reach Slack, GitHub, and Linear.
  - [Reliability](https://agentrelay.com/docs/relayflows/reliability): The crash-resume contract and the closed failure vocabulary.
