GuidesConvex
Swarms
Experimental cross-action swarm routing for Convex.
Convex Swarms
createComponentSwarm() from @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 @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 '@crux/convex/server'
import { createComponentSwarm } from '@crux/convex/swarm'
import { generate } from '@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.