In Stately Agent, conditional logic is handled via guarded transitions rather than router functions.
Instead of a node returning a string that a router function uses to pick the next node, the model selects from a set of allowedEvents. The transition is then governed by a guard (a function attached to an event). If the guard returns undefined, that transition is considered illegal for the current state.
**Key differences:
- Model interaction: The model picks a named event. If the guard rejects it, the attempt is recorded as
rejected-by-guard and the model is prompted again with that feedback. - Safety: Constraints are enforced by the machine's guards, meaning they hold regardless of what the prompt instructs the model to do.
- Typo prevention: The model picks from defined event names rather than generating arbitrary routing strings.
// Example of a guarded transition in a machine definition
grading: {
invoke: {
src: "agent.decide",
input: ({ context }) => ({
model: "grader",
system: "GENERATE if the documents answer the question, else REWRITE.",
prompt: `Question:\n${context.question}\n\nDocuments:\n${context.docs}`,
allowedEvents: ["GENERATE", "REWRITE"],
}),
},
on: {
GENERATE: { target: "generating" },
// The guard: returns undefined if the condition isn't met, making the transition illegal
REWRITE: ({ context }) =>
context.rewrites < 2
? { target: "rewriting", context: { rewrites: context.rewrites + 1 } }
: undefined,
},
}