Routes
A route is a folder under src/app/. Its path becomes the agent endpoint. A route's index.ts exports exactly one entry shape: agent, workflow, graph, or chain.
Route entry
Every route has an index.ts that exports one of:
agent— an LLM-driven flow with auto-discovered tools. The default scaffold export. See Agents.workflow— a deterministic async function with typed state. See Routes / workflow below.graph— a LangGraph graph. Branching, looping, conditional edges.chain— a LangChain LCELRunnable. Simple linear pipelines.
Pathname rules
Routes follow the same conventions as the Next.js App Router:
(group)/— route group, excluded from the path[segment]/— dynamic segment, preserved in the route id and exposed in generated route params[...rest]/— catch-all[[...optional]]/— optional catch-all
Example tree:
src/app/
(public)/
hello/
[tenant]/
index.ts ← exports agent | workflow | graph | chain
state.ts ← optional, route state schema
tools/
greet.ts ← auto-discovered for agents and RuntimeContextThe (public)/ segment is excluded from the path, so this route is /hello/[tenant].
workflow
A workflow is a deterministic async function. The first argument is the typed route state; the second is a RuntimeContext that exposes the route's discovered tools.
import type { RuntimeContext } from "@dawn-ai/sdk"
import type { RouteTools } from "dawn:routes"
import type { z } from "zod"
import type state from "./state.js"
type HelloState = z.infer<typeof state> & { readonly tenant: string }
export async function workflow(
state: HelloState,
ctx: RuntimeContext<RouteTools<"/hello/[tenant]">>,
) {
const r = await ctx.tools.greet({ tenant: state.tenant })
return { ...state, greeting: r.greeting }
}The RouteTools<"/hello/[tenant]"> generic resolves to the union of tools auto-discovered in this route's tools/ directory.
graph
Export a compiled LangGraph graph as a named graph export:
import { StateGraph, START, END } from "@langchain/langgraph"
import type { HelloState } from "./state.js"
export const graph = new StateGraph<HelloState>({ channels: { /* ... */ } })
.addNode("greet", async (state) => state)
.addEdge(START, "greet")
.addEdge("greet", END)
.compile()The graph receives the same typed state and runs through Dawn's runtime. State channels still need to be declared (LangGraph requirement).
chain
Export a LangChain LCEL Runnable as a named chain export:
import { RunnableSequence } from "@langchain/core/runnables"
export const chain = RunnableSequence.from([
// chain steps
])Running a route
Routes dispatch by path:
$ echo '{"tenant":"acme"}' | dawn run '/hello/[tenant]'
$ dawn dev # HMR + dev serverProgrammatic dispatch is also available — see Dev Server for the runtime API and the /runs/wait / /runs/stream endpoints.