Reducers
Agents never mutate state directly. Instead, they emit actions, and pure reducer functions merge those actions into the existing state.
The pattern
Section titled “The pattern”(CurrentState, Action) → NewStateThis is the same pattern used by Redux, Elm, and event sourcing systems. Every state change is explicit, traceable, and reproducible.
Why reducers?
Section titled “Why reducers?”- No race conditions — parallel nodes can’t corrupt shared state because they don’t touch it directly
- Full auditability — every state change is tied to an action with a source node
- Crash recovery — state is a deterministic function of all actions applied so far
- Permission enforcement — the reducer validates that the agent only wrote to its allowed
write_keys
How it works
Section titled “How it works”- An agent calls
save_to_memory({ key: "notes", value: "..." }) - The executor packages this into an
Action:{type: 'SAVE_MEMORY',node_id: 'researcher',key: 'notes',value: '...',} - The reducer validates
write_keysand merges:function reducer(state: WorkflowState, action: Action): WorkflowState {// Validate: is 'notes' in the agent's write_keys?// Merge: state.memory.notes = action.valuereturn { ...state, memory: { ...state.memory, [action.key]: action.value } };} - The new state is persisted and execution continues
Taint propagation
Section titled “Taint propagation”If an agent reads any tainted keys (data from external tools), the reducer automatically marks its output keys as tainted too. This propagation ensures downstream nodes can make trust decisions about their inputs.
Best practices
Section titled “Best practices”Agents are data, not classes:
// ❌ Bad: class-based agentclass ResearcherAgent extends BaseAgent { ... }
// ✅ Good: config-driven agentconst ResearcherConfig = { id: "researcher", model: "claude-sonnet-4-20250514", system_prompt: "You are a...", tools: [{ type: "mcp", server_id: "web-search" }],};Schema-first validation: Every input and output should have a Zod schema:
// Tool inputsparameters: z.object({ query: z.string() })
// Agent configsAgentConfigSchema.parse(config)Next steps
Section titled “Next steps”- Agents — how agents produce actions
- Workflow State — the shared state that reducers update
- Security — how write_keys and taint tracking enforce Zero Trust