dawn

Skip ahead — hand this to your coding agent

Add a type-inferred tool to an existing route.

Tools

Tools are the units of work a route's entry can invoke. They live in a tools/ subdirectory inside a route, are discovered automatically, and have their types inferred from source — no Zod schemas, no manual type wiring.

A minimal tool

// src/app/(public)/hello/[tenant]/tools/greet.ts
export default async (input: { readonly tenant: string }) => {
  return { greeting: `Hello, ${input.tenant}!` }
}

That's it. Dawn extracts the input and output types at build time using the TypeScript compiler API and writes them into dawn.generated.d.ts. The tool becomes available as ctx.tools.greet inside the route's entry, fully typed.

Invoking a tool

// in the route's index.ts
export async function workflow(state, ctx) {
  const result = await ctx.tools.greet({ tenant: state.tenant })
  return { ...state, greeting: result.greeting }
}

ctx.tools.greet has full IntelliSense — input shape, return shape, everything.

Input and output rules

  1. 1

    Use a typed input parameter

    Annotate the input parameter with an inline type. This is what Dawn's compiler pass extracts.

    export default async (input: { readonly tenant: string; readonly limit?: number }) => { ... }
    
  2. 2

    Mark fields readonly

    readonly is preserved through type generation. Use it on every field — tool inputs are pure data, never mutated.

  3. 3

    Keep inputs and outputs JSON-serializable

    Dawn serializes through the runtime boundary. Classes, Dates, Maps, functions — none of them survive. Use primitives, plain objects, and arrays.

  4. 4

    Return a plain object

    Always return an object literal, not a primitive or array. Dawn's type inference handles shape-based returns cleanly; a bare return someString muddles the generated types.

The generated declaration

Dawn writes a dawn.generated.d.ts file at your app root that looks roughly like:

declare module "dawn:routes" {
  export type RouteTools<P> = DawnRouteTools[P]
  // greet signature inferred from tools/greet.ts export
}

Regenerate manually if needed:

dawn typegen

Common patterns

  • External API wrappers — one tool per endpoint. Input shape mirrors the endpoint's parameters; output is the parsed response.
  • Database reads — input includes the filters; output is the record(s). Keep queries cheap — tools are meant to be fast.
  • LLM calls — input is the prompt variables; output is the parsed model response. Defer orchestration to the route entry.
  • Pure transformations — no side effects, just shape-to-shape mapping. Dawn's testing makes these trivial to verify.