<!-- Generated by scripts/gen-docs.ts from the TSDoc in src/. Do not edit by hand. -->

# `workflows/swarm`

Source: [`src/workflows/swarm.ts`](https://github.com/AI-for-dev/combo/blob/main/src/workflows/swarm.ts)

Several members on one job, for as many rounds as you allow.

Every other combinator here decides who does what: `fanOut` hands out the
subtasks, `orchestrate` has a planner write them, `chain` fixes the order.
A swarm decides none of it. The members are told the same goal, given a
{@link Board} to talk on and a {@link Claims} to take work from, and what
they divide between them is theirs.

That makes it the one combinator whose value is an open question, so it is
built to be compared against the thing it might not beat: **with no board, no
claims and one round, a swarm is a fan-out**. That degenerate case is a test,
and it is the control arm of any experiment run with this.

Three things are decided here rather than left to a prompt, each one measured
on a run before it was written down:

- **Members remember.** `lifetime` defaults to `"workflow"` here and nowhere
  else. A member that forgets the last round cannot build on what it saw, and
  a swarm of amnesiacs is a fan-out that costs more.
- **What is new is handed over, not fetched.** A member is given what the
  board has for it at the top of its task. Measured: given something that
  arbitrates, members stopped reading the board entirely - they take, they are
  refused, they take something else. Making them spend a call to find out what
  happened would be charging them for the workflow's bookkeeping.
- **A member that is gone holds nothing.** Whatever it still had is released
  when it drops out or when the swarm ends, and the result says what that was.
  Measured: two of three members never released what they took, and claims
  left by a member that has closed are work nobody will do and nobody can
  take.

## `membersOutput`

*function*

```typescript
export function membersOutput(members: readonly SwarmMember[]): string { /* … */ }
```

What every member last said, under the name it posted under.

The id and not the agent, because three copies of one agent share the name
and a reader finds a member on the board by its id.

## `MemberSpec`

*type*

```typescript
export type MemberSpec = {
	/** Whose copies these are. */
	agent: Agent;
	/** How many copies of it. Each gets its own id, its own memory, its own place. */
	count: number;
};
```

How many of one agent stand on the board. A swarm of one is a run.

## `swarm`

*function*

```typescript
export async function swarm(options: SwarmOptions): Promise<SwarmResult> { /* … */ }
```

Runs a swarm.

A round is one `ask` per live member, through `mapConcurrent`. A member whose
turn fails is asked again the next round; two failed turns in a row and it
drops out rather than costing every remaining round, keeping the turn that
failed as its result.

The pool closes everything in a `finally`, cancellation included, and the
claims of whoever is gone are released before the result is built.

## `SwarmClaim`

*type*

```typescript
export type SwarmClaim = {
	/** The thing that was taken. */
	key: string;
	/** The member that had it when the swarm ended. */
	heldBy: string;
	/** True when the swarm released it because the member was gone. */
	released: boolean;
};
```

What a member was holding when it stopped, and whether anyone took it back.

## `SwarmEnd`

*type*

```typescript
export type SwarmEnd = "until" | "rounds" | "members" | "signal";
```

Why the swarm stopped, apart from whether it went well.

## `SwarmMember`

*type*

```typescript
export type SwarmMember = {
	/** The subagent id, which is also the name it posts and claims under. */
	id: string;
	/** Which agent it is a copy of. */
	agent: string;
	/** Its last turn. A member that dropped out early keeps the turn that failed. */
	result: Result;
};
```

One member, and the last thing it said.

## `SwarmOptions`

*type*

```typescript
export type SwarmOptions = WorkflowOptions & {
	/** Who is on it, and how many of each. */
	members: readonly MemberSpec[];
	/** The one thing every member is told. Broad enough to admit several routes. */
	goal: string;
	/** Turns per member. Defaults to 3, because "forever" must not be reachable. */
	rounds?: number;
	/** Members asked at once. Defaults to 4. */
	concurrency?: number;
	/** Defaults to a fresh in-memory board. Pass one to read it afterwards. */
	board?: Board;
	/** What there is to take. Absent, the members are on their honour. */
	claims?: Claims;
	/** Has the goal been reached? Read from the board, after every round. */
	until?: (board: Board) => boolean;
	/**
	 * How many `result`s a member may post in one turn. Absent, as many as the
	 * board takes: a member describing files posts one per file. A debate wants
	 * 1, because a vote posted again is not an argument.
	 */
	resultsPerTurn?: number;
};
```

Who is on it, what they are told, and how long they have.

## `SwarmResult`

*type*

```typescript
export type SwarmResult = WorkflowResult & {
	/** One per member, in roster order, the ones that failed included. */
	members: readonly SwarmMember[];
	/** Every post, in order. The run's social history. */
	posts: readonly Post[];
	/** What was taken, and what was still held when its holder stopped. */
	claims: readonly SwarmClaim[];
	/** How many rounds actually ran. */
	rounds: number;
	/** Whether `until` fired. Reaching the round cap is not success. */
	converged: boolean;
	/** Which cap ended it. */
	stoppedBy: SwarmEnd;
};
```

What the swarm did, and what it left behind.

As a `Result`: every member's last turn, labelled by the name it posted
under, a failed one marked as such; `ok` is false when any member failed or
was never asked, `error` the first failure's. `steps` is every turn of every
round, and `usage` their sum over the swarm's own wall time.
