GuidesConvex
Swarms
Experimental cross-action swarm routing for Convex.
createComponentSwarm() from @use-crux/convex/swarm is experimental. It explores cross-action swarm routing by running one agent turn per scheduled action, storing state in the Crux component, and scheduling the next turn after a handoff.
For launch-critical code, keep swarms as immediate adapter compositions inside one Crux-aware action. Use flow() from @use-crux/convex/server when you need durable Convex orchestration, external signals, or resumable multi-step work.
Define The Swarm Runner
"use node";
import { action, internalAction, query } from "@use-crux/convex/server";
import { createComponentSwarm } from "@use-crux/convex/swarm";
import { generate } from "@use-crux/ai";
import { openai } from "@ai-sdk/openai";
import { components, internal } from "../_generated/api";
import { v } from "convex/values";
import { triage, research, writer } from "../prompts/agents";
const model = openai("gpt-4o");
const swarm = createComponentSwarm({
component: components.crux,
generate: (prompt, opts) => generate(prompt, { ...opts, model }),
});Start
export const start = action({
args: { message: v.string() },
handler: async (ctx, args) => {
return swarm.start(ctx, {
agents: { triage, research, writer },
startAgent: "triage",
input: { message: args.message },
resumeAction: internal.workflows.swarm.resume,
maxHandoffs: 8,
});
},
});Resume
export const resume = internalAction({
args: { swarmRunId: v.string() },
handler: (ctx, args) =>
swarm.resume(ctx, args.swarmRunId, {
agents: { triage, research, writer },
resumeAction: internal.workflows.swarm.resume,
}),
});Inspect State
export const getState = query({
args: { swarmRunId: v.string() },
handler: (ctx, args) => swarm.getState(ctx, args.swarmRunId),
});How It Works
start()opens or joins a Crux run and stores initial swarm state.- The current agent runs with transfer tools injected.
- If there is a handoff, state is updated and the resume action is scheduled.
resume()restores the stored observability context so turns are siblings in the same run.- When the swarm completes or errors, the run is ended.
Best Practices
- Treat
createComponentSwarm()as experimental until durable swarm semantics are finalized. - Keep agent IDs stable because state and handoff paths use those IDs.
- Keep
resumeas an internal action. - Set
maxHandoffsto prevent runaway routing. - Use
history: 'transfer-only'by default; useaccumulateonly when each agent needs prior outputs.