State
Routes may declare a state contract in a colocated state.ts file. State is the JSON payload passed to the route entry and returned by the route runtime.
The shape
When present, state.ts default-exports a Zod schema or Standard Schema value. Agent routes use it to parse defaults at runtime, and you can derive the TypeScript type via z.infer<typeof state>:
import { z } from "zod"
export default z.object({
/** Accumulated context from tool call results */
context: z.string().default(""),
})Dynamic segments are route params
If your route's directory contains a dynamic segment like [tenant], Dawn records that segment in generated route metadata. Today, route execution is input-driven: pass the value in the JSON input you send to dawn run, /runs/wait, or /runs/stream.
src/app/(public)/hello/[tenant]/ → route id /hello/[tenant]For example:
echo '{"tenant":"acme"}' | dawn run '/hello/[tenant]'The schema covers state your entry reads or the caller supplies. Do not rely on a concrete pathname with an inline tenant value to populate tenant; the current resolver matches the parameterized route id (/hello/[tenant]) or the route entry file path.
Custom reducers
Each route may include a reducers/ directory with one file per state field. The default export is a (current, incoming) => merged function that overrides Dawn's default merge for that field.
export default (current: string, incoming: string) =>
current ? `${current}\n${incoming}` : incomingThe file basename must equal the state-field name (context.ts → reduces state.context). Reducers are useful for accumulation patterns (concatenating logs, summing counters, deduplicating lists) where the default object spread is too coarse.
Rules
- 1
State must be JSON-serializable
Dawn serializes state across runtime boundaries (dev server, scenario tests, LangSmith). Use primitives, plain objects, arrays. No classes, no Dates, no Maps.
- 2
Prefer readonly
Mark fields
readonly(or use Zod's.readonly()) when they should not be mutated. The entry function returns a new state object rather than mutating the input. This keeps scenario tests deterministic and LangGraph checkpoint-safe. - 3
Outputs accumulate
A workflow can return
{ ...state, newField }when you want to preserve incoming state. Dawn returns the route output; it does not automatically merge workflow output with the input for you.
State flow
State crosses the runtime boundary at every entry point:
dawn run— JSON via stdin, JSON via stdout./runs/waitand/runs/stream(locally underdawn dev, on LangSmith in prod) — JSON over HTTP keyed byassistant_id.
Tool results, intermediate values, and other in-route data live inside the entry for the duration of a single run; only the route result crosses the boundary.