subagentSwarm
Bounded fan-out worker pool over any Chatbot. You supply the workers, the decompose function, and an optional reduce. The recipe enforces a maxFanOut concurrency cap and an optional per-root-turn USD cost ceiling — the application-layer defense for the runaway-loop case.
subagentSwarm composes a pool of worker Chatbot instances
into a bounded-parallel work queue. You pass the workers (any
recipe-returned Chatbot satisfies the contract — simpleChatbot,
observableChatbot, compliantChatbot, even custom shapes that
implement ask()) plus a decompose(userTask, workerCount) that
maps the user task to per-worker prompts. The recipe runs the
prompts under a maxFanOut concurrency cap, polls an optional
cost ceiling between worker launches, and reduces the results.
Best fit: a sub-agent swarm consumer. The defining pain is the runaway-loop case: a buggy fan-out or stuck sequential loop can spend $10K in one user turn. This recipe bakes the two application-layer guardrails.
Quickstart
import { simpleChatbot } from "@pleach/recipes/chatbot";
import { subagentSwarm } from "@pleach/recipes/subagent-swarm";
const swarm = subagentSwarm({
workers: [
simpleChatbot({ systemPrompt: "You research equities. Be terse." }),
simpleChatbot({ systemPrompt: "You research equities. Be terse." }),
simpleChatbot({ systemPrompt: "You research equities. Be terse." }),
],
maxFanOut: 2, // at most 2 workers in flight at once
decompose: (task, workerCount) => {
const symbols = ["AAPL", "GOOG", "MSFT"];
return symbols.slice(0, workerCount).map((s) => `${task}: ${s}`);
},
reduce: (results) => results.map((r) => `- ${r}`).join("\n"),
// Optional: application-layer runaway-loop guardrail
perRootTurnCostCeilingUsd: 50,
costReporter: () => myProviderCostMeter.getTotalUsd(),
});
console.log(await swarm.dispatch("Research big-tech earnings"));What it does
dispatch(userTask) runs the following sequence:
- Call
decompose(userTask, workers.length)to get the per-worker prompts. - Walk the prompts in order; launch at most
maxFanOutconcurrent workers (Math.max(1, Math.min(maxFanOut, workers.length))). - Before launching each new worker, poll
costReporter()(if supplied) and compare againstperRootTurnCostCeilingUsd. Over-ceiling stops launches; already-running workers finish. - Pass surviving results (output-order preserved) to
reduce(results, userTask). The default reduce is newline-join.
maxFanOut defaults to 1 (sequential) to preserve replay
determinism for buyers who haven't yet adopted the greenfield
runtime with byte-identical spawn-order normalization. Swarm
buyers tuning for throughput should set explicitly.
Config reference
interface SubagentSwarmConfig {
workers: readonly Chatbot[];
maxFanOut?: number; // default 1
decompose: (
userTask: string,
workerCount: number,
) => readonly string[] | Promise<readonly string[]>;
reduce?: (results: readonly string[], userTask: string) => string;
perRootTurnCostCeilingUsd?: number;
costReporter?: () => number | Promise<number>;
}
interface SubagentSwarm {
readonly workers: readonly Chatbot[];
dispatch(userTask: string): Promise<string>;
reset(): Promise<void>;
}Common gotchas
workersmust be non-empty. Construction throws otherwise.- Excess decompose prompts are dropped with a console
warning. If
decomposereturns more prompts than there are workers, the recipe does not silently round-robin — the excess are dropped and a warning fires. - The cost ceiling is application-layer best-effort. This
is the OSS-tier defense. The load-bearing swarm guardrail is
@pleach/gateway'smaxCostUsdPerRootTurn(the commercial SKU). If a worker is mid-flight when the ceiling crosses, it still runs to completion — the ceiling only gates new launches. costReporteris consumer-owned. The recipe is provider-agnostic. WirecostReporterto your provider's per-call cost report or your billing meter. Omitting bothcostReporterandperRootTurnCostCeilingUsddisables ceiling enforcement entirely.- Default
maxFanOut: 1is sequential. Set explicitly when tuning for throughput. Higher values trade determinism for latency. - Worker sessions are NOT reset between dispatches by
default. Workers keep their conversational state across
dispatch()calls. Callawait swarm.reset()to force a fresh session on every worker on the next dispatch. - No
@pleach/core/spawnsubstrate. This recipe does not implementspawn / race / waitAll / waitN / boundedor theSpawnEventschema — those are v1.x core-substrate changes.
See also
@pleach/recipesoverview — every recipe in one page.Swarm agent— full-system pattern.@pleach/gateway— the commercial SKU whosemaxCostUsdPerRootTurnis the load-bearing runaway- loop guardrail.Subagents— the underlying attribution primitive.
verticalAgent
Chatbot pre-wired with your domain plugin. You write the HarnessPlugin (citation extraction, entity recognition, post-tool-tier enrichment, custom event channels); the recipe wires it into createPleachRuntime so every ask() turn picks up your domain logic without manual plumbing.
enterpriseAgent
Compliance scrubber + sub-agent attribution + procurement-visible permittedFamilies envelope, stacked into one factory. The frontier-lab direct-enterprise recipe — two of the three pillars wired in-process; the third (gateway-side family failover) reads the envelope at runtime.