Bahini
Open app

Core

Swarms

A swarm runs many agents together — a flat parallel fan-out or a coordinator-planned DAG with dependencies, conditional edges, bounded concurrency, and self-heal replan. Define one inline, or save a preset and run it repeatedly.

Run a swarm inline#

A coordinator plans and delegates to workers; an optional synthesizer folds the results. Guardrails set here are inherited by every member.

 
ts
const { runId } = await bahini.runSwarm({
  coordinatorAgentId: "agent_coord",
  workerAgentIds: ["agent_scout", "agent_analyst", "agent_writer"],
  synthesizerAgentId: "agent_editor",
  prompt: "Produce the weekly BI digest.",
  guardrails: { guards: ["pii", "secrets", "injection"], mode: "enforce" },
  concurrency: 2,       // parallelism cap
  swarmMaxTokens: 500_000, // hard cost cap for the whole run
});

Wire a DAG#

Pass a plan to shape the workers into a graph. Each node names a worker by key with dependsOn (hard — a failed upstream skips the node) and optionalDependsOn (soft — the node still runs on partial inputs). A condition makes a conditional edge for dynamic branching.

 
ts
const { runId } = await bahini.runSwarm({
  coordinatorAgentId: "agent_coord",
  workerAgentIds: ["scout", "analyst", "writer"],
  prompt: "Draft the competitor brief.",
  plan: [
    { id: "a", agent: "scout" },
    { id: "b", agent: "analyst", dependsOn: ["a"] },
    { id: "c", agent: "writer",  dependsOn: ["b"],
      condition: { node: "b", contains: "PRICE_CHANGE" } },
  ],
});
A malformed plan is ignored server-side (it falls back to a flat fan-out) so it never wedges a run. Validate ahead of time in CI with validateSwarmPlan — it returns { ok: false, errors } naming unknown agents, duplicate ids, unknown deps, or cycles.

Watch the graph#

getRunDagjoins the persisted plan with each node's live status — poll it (or pair with waitForRun) to render the graph as nodes go running → completed / failed / skipped. Returns null for a non-coordinator run.

 
ts
const dag = await bahini.getRunDag(runId);
for (const node of dag?.nodes ?? []) {
  console.log(node.id, node.agentName, node.status);
}

Presets (reusable swarms)#

createSwarmPreset(input)

Save a swarm's shape once, run it repeatedly with runPreset. kind: "basic" runs workers in parallel; "coordinator" adds an orchestrator.

 
ts
const { id } = await bahini.createSwarmPreset({
  key: "weekly-bi",
  name: "Weekly BI",
  kind: "coordinator",
  coordinatorAgentId: "agent_coord",
  workerAgentIds: ["scout", "analyst", "writer"],
});

// Persist a guardrail policy so every run of the preset is governed:
await bahini.setPresetGuardrails(id, { guards: ["pii"], mode: "enforce" });

await bahini.runPreset(id, { prompt: "Run this week's digest." });