Skip to content

Workflows & teams

Workflows compose agents into durable, restartable runs. Teams add an opt-in coordination layer on top of the same durable engine.

A workflow definition owns the input, output, event and limit contracts, and requires a DurableRunStore.

import { defineWorkflow } from '@fevex/core';
import { z } from 'zod';
const review = defineWorkflow({
name: 'review',
version: '2',
inputSchema: z.object({ draft: z.string() }),
outputSchema: z.string(),
events: {
'review.approved': {
payloadSchema: z.object({ approved: z.literal(true) }),
requireActor: true,
},
},
limits: { maxSteps: 8, maxToolCalls: 12 },
async run(step, input) {
const draft = await step.agent('draft', 'writer', { input });
const approval = await step.waitForEvent('approval', 'review.approved');
return `${draft.output} (${approval.actor?.id})`;
},
});

Input is validated before the initial checkpoint is stored, and output is always validated with the definition schema — including after a pause or recovery. stepId is the durable identity of a step: keep it stable while its meaning is stable, and increment version when replay order or step meanings change. External events must be declared. This example requires an approval payload of { approved: true } and an actor when the workflow resumes.

Paused runs resume with resumeRun; an external worker can recover an orphaned running run after its lease expires:

await app.recoverRun(runId, {
actor: { id: 'recovery-worker', type: 'service' },
});

FEVEX does not poll globally: deployments need an external orphan detector and a scheduler that calls resumeRun for elapsed timers.

Teams reuse registered agents under a supervisor and cannot widen an agent’s model, tools, policies, sandbox or limits.

import { defineTeam } from '@fevex/core';
const softwareTeam = defineTeam({
name: 'software-team',
version: '1',
supervisor: 'planner',
members: [
{ agent: 'researcher', role: 'research' },
{ agent: 'coder', role: 'implementation' },
{ agent: 'reviewer', role: 'review' },
],
limits: { maxDelegations: 12, maxParallel: 2 },
async run(team, input) {
// coordinate members…
},
});

For the full durable-execution, compensation and cancellation semantics, see the core README.