This page covers the state schema: the typed fields a flow collects. A flow also needs a place to persist that state between MCP calls. See Persistence, which is required for any flow to work.Documentation Index
Fetch the complete documentation index at: https://docs.waniwani.ai/llms.txt
Use this file to discover all available pages before exploring further.
State schema
Every flow declaresstate as a map of field names to Zod schemas. The runtime state type is inferred from this map, so ctx.state is fully typed inside every node. Field descriptions also feed the model’s tool description, so it knows what to collect.
State is always partial
At any point during execution, only the fields filled by earlier nodes are populated. The engine typesctx.state as Partial<TState>, so always guard when reading fields.
Field types
Prefer constrained types. The more constrained a field, the better the model fills it.| Field shape | Use |
|---|---|
| Fixed choices | z.enum([...]) |
| Free-form text | z.string() |
| Bounded numbers | z.number().min(0).max(100) |
| Booleans | z.boolean() |
| Lists | z.array(z.string()) |
| Nested group (1 deep) | z.object({ ... }) |
Enums pair well with suggestions
suggestions will lead the model to offer exactly those and validate the answer against the enum.
Nested state
Usez.object() when a sub-entity has multiple fields. Only one level of nesting is supported.
{ driver: { name: "John" } } does not wipe driver.licenseNumber.
Pre-filling from the user’s message
When the user’s opening message already contains answers (for example, “I’m in 75011, quote me a half-day lesson”), the model can pass them instateUpdates on the very first call:
undefined, null, or "" do not count as filled). No extra configuration is needed. Just make sure field descriptions are clear enough for the model to map message content to field names.
How state updates happen
Nodes update state in three places:- Action nodes return a plain object that is deep-merged into state.
- Interrupts store the user’s answer at the field key used in
interrupt({ field: { question } }). - Interrupt validators (see Interrupts) can return additional state updates after a user answer passes validation.