Dev Server
dawn dev starts a local runtime with Agent Protocol (AP) HTTP endpoints. It also exposes an AG-UI SSE endpoint for web clients such as CopilotKit. It watches route and tool files, regenerates types when signatures change, and restarts the child route runtime for structural changes.
Starting the server
dawn devBy default the server binds an ephemeral localhost port (announced on stdout as Dawn dev ready at http://127.0.0.1:<port>). Pass --port <n> for a stable port:
dawn dev --port 3001The bind address is always 127.0.0.1.
Invoking a route
With the server running in one terminal, you can hit it from another:
echo '{"messages":[{"role":"user","content":"Hello"}]}' | dawn run '/research' --url http://127.0.0.1:3001dawn run resolves the route id to a route key of the form <routeId>#<kind> (e.g. /research#agent), POSTs to /threads/<t-cli-uuid>/runs/wait with {route, input}, and prints the result.
Agent Protocol endpoints
The dev server exposes eight endpoints organized around a thread lifecycle: create thread → run (wait or stream) → read state → resume. Anything else returns 404. Thread state persists in SQLite under .dawn/ and survives server restarts — see Configuration for the checkpointer and threadsStore defaults and override options.
Thread lifecycle with curl
The following sequence creates a thread, runs a route, and reads the final state:
# 1. Create a thread (body is optional)
curl -X POST http://127.0.0.1:3001/threads \
-H 'content-type: application/json' \
-d '{}'
# -> { "thread_id": "...", "created_at": "...", "updated_at": "...", "metadata": {}, "status": "idle" }
THREAD_ID="<thread_id from above>"
# 2. Run the route (blocking)
curl -X POST http://127.0.0.1:3001/threads/$THREAD_ID/runs/wait \
-H 'content-type: application/json' \
-d '{
"route": "/research#agent",
"input": { "messages": [{ "role": "user", "content": "What is LangGraph?" }] }
}'
# -> final state JSON
# 3. Read the latest checkpoint state
curl http://127.0.0.1:3001/threads/$THREAD_ID/stateReadiness check. Bypasses middleware.
GET /healthz
-> 200 { "status": "ready" }Returns 200 once the child runtime is up; non-200 means not-ready. dawn dev itself uses this endpoint internally to detect readiness.
The route format <routeId>#<kind> is the same format dawn build writes into the graphs map of .dawn/build/langgraph.json, so the same client request body works against dawn dev and against a deployed runtime.
AG-UI endpoint
For UI clients that speak AG-UI, dawn dev also serves:
POST /agui/{routeId}
content-type: application/json
accept: text/event-streamThe body must be an AG-UI RunAgentInput. Dawn creates the thread when the incoming threadId is new, maps the newest user message to the route input, streams the route, and translates Dawn chunks into AG-UI events with @dawn-ai/ag-ui.
The URL segment after /agui/ is the URL-encoded Dawn assistant id (<routeId>#<kind>):
Dawn route key: /chat#agent
AG-UI URL: /agui/%2Fchat%23agentThe endpoint emits RUN_STARTED, assistant text events, tool call/result events, and one terminal RUN_FINISHED or RUN_ERROR. A parked run uses the standard AG-UI interrupt outcome on RUN_FINISHED; successful runs use the success outcome. Planning and subagent capability events have no v1 AG-UI mapping and are ignored.
Human-in-the-loop answers use the top-level AG-UI RunAgentInput.resume array. Every answer is addressed to one pending interrupt:
{
"threadId": "thread-1",
"runId": "run-2",
"messages": [],
"tools": [],
"context": [],
"state": {},
"resume": [
{ "interruptId": "perm-abc123", "status": "resolved", "payload": "once" }
]
}The chat example includes a CopilotKit v2 client wired through this endpoint; see examples/chat/web and the @dawn-ai/ag-ui package README.
Tracing
If LANGSMITH_API_KEY is present in the loaded environment and LANGCHAIN_TRACING_V2 is not already set, dawn dev automatically sets LANGCHAIN_TRACING_V2=true. Set LANGCHAIN_PROJECT to name the LangSmith project used for traces. See Observability for a full walkthrough, including how to read traces and the HITL interrupt gotcha.
Middleware
src/middleware.ts (default-exporting a function returned by defineMiddleware) gates Agent Protocol /runs/stream, /runs/wait, and /resume execution plus AG-UI route execution under both dawn dev and the built runtime served by dawn start. Thread create, read, delete, and state endpoints do not invoke middleware. It can short-circuit execution with reject(status, body?), or continue with allow(context?) — the optional context flows to every tool as ctx.middleware (a Readonly<Record<string, unknown>>).
MiddlewareRequest shape: { assistantId, headers, method, params, routeId, url }. See Middleware for the full reference.
Hot reload
When you save a file under src/app/, the dev server:
- 1
Reclassifies the change
Typegen-only changes (tool signatures, state schemas) trigger a debounced typegen run (~100ms) — the child does not restart. Structural changes restart the child.
- 2
Restarts the child runtime when needed
Dawn uses a parent-child process architecture. The parent owns the HTTP server and file watcher; the child owns the route graph. On a structural change the child is
stop()ed (with a forced kill timeout — in-flight requests are given a brief grace window, then force-killed) and a fresh child is started. - 3
Preserves route ids
Routes keep their stable ids (
<routeId>#<kind>) across restarts, so any agent or harness holding aroutekey continues to work without reconnection.
Logging
The parent process keeps the HTTP server alive while child route runtimes restart. Set DAWN_DEV_SHUTDOWN_TIMEOUT_MS to override the child-restart grace window.