Dawn AI is now b4. You can pronounce it “bee-four” or “b4 dot run.”
I started Dawn to solve a problem I kept having. Every time I began a new agent project I had to build the harness again. I had to decide where the agent belonged, how tools should be organized, how to save a thread, and how I was going to test the whole thing. I wanted to get to the idea, but first I had to rebuild the project around the idea.
After doing this a few times I decided to build a starting point for myself. That starting point became Dawn.
Now it has a new name.
Why rename Dawn?
Dawn AI was difficult to find. There are other products and companies named Dawn, including several working in AI. If I told someone about Dawn there was a good chance they would find something else. That’s a problem for a project that people mostly discover through a link, a search, or a conversation with another developer.
I also used to describe Dawn as “Next.js for agents.” It was a helpful shortcut at the time. I was building a TypeScript framework with a familiar project structure, file conventions, generated types, a development server, and deployment targets. If you had used Next.js, you had a pretty good idea of what I was trying to do.
Agent development has changed since then. Eve is a filesystem-first framework for durable agents. Flue provides a programmable TypeScript agent harness. They are separate projects, but their descriptions point in a similar direction. Several of us are using files and a harness to solve some of the same problems.
A filesystem and a reusable harness are becoming common ways to build agents. So, “Next.js for agents” doesn’t say very much anymore.
There are also a lot of agent frameworks. I mean, a lot. Another serious name in a long list of serious names didn’t sound like much fun.
Why b4?
Friends have called me “blove” for a long time. The name is pretty simple: b for Brian followed by my last name, Love.
Love has four letters. So, b plus four becomes b4.
It’s a little nerdy, personal, and playful. I like that.
You may have also noticed names such as T3, a16z, and n8n. b4 fits right in, and it is easy to say and remember. In a crowded field, a short and fun name feels right for the project.
What is b4?
b4 is a TypeScript meta-framework for building agents with LangGraph.
The term meta-framework can sound more complicated than it is. LangGraph provides the graph and agent runtime. b4 provides the application structure around it. It takes care of many of the choices I was making again and again at the beginning of a project.
First, scaffold an app.
The rename is in progress, so today’s packages and CLI still use the Dawn name.
Do not go looking for @b4/* packages or a b4 command yet.
You will need Node 24 or newer.
npm create dawn-ai-app@latest my-agent
cd my-agent
npm install
Next, add a support route and one tool:
src/app/support/
index.ts
tools/
searchDocs.ts
The route is an agent descriptor:
// src/app/support/index.ts
import { agent } from '@dawn-ai/sdk';
export default agent({
model: 'gpt-5-mini',
systemPrompt:
'Research support questions using the available documentation. Return a concise answer and cite the matching document titles.',
});
agent() builds the model-driven LangGraph route.
The model can decide when the support question needs a tool, and the system prompt gives it a specific research job.
Now give it something deterministic to call:
// src/app/support/tools/searchDocs.ts
export default async ({ query }: { readonly query: string }) => {
const documents = [
{
title: 'Passwords',
text: 'Reset your password from the sign-in screen with Forgot password.',
},
{ title: 'Refunds', text: 'Annual plans can be refunded within 30 days.' },
{
title: 'Teams',
text: 'Owners can invite members from the team settings.',
},
];
const stopWords = new Set(['how', 'the', 'with', 'from']);
const words = (text: string) =>
(text.toLowerCase().match(/[a-z0-9]+/g) ?? []).filter(
(word) => word.length > 2 && !stopWords.has(word),
);
const queryWords = words(query);
return documents.filter((document) => {
const documentWords = new Set(words(document.text));
return queryWords.some((word) => documentWords.has(word));
});
};
The filename becomes the tool name. Its parameter is ordinary TypeScript, and the returned array stays small enough to inspect while learning the convention.
There is no registry to update and no schema library to add.
Filesystem discovery sees tools/searchDocs.ts and wires searchDocs into the /support route.
Type generation reads the function signature and writes the route and tool contracts to .dawn/dawn.generated.d.ts.
The TypeScript source is the contract.
Run the route directly from the terminal:
echo '{"messages":[{"role":"user","content":"How do I reset my password?"}]}' | npx dawn run '/support'
The command prints a RuntimeExecutionResult as JSON.
Here is representative stdout with the paths and timing fields trimmed:
{
"status": "passed",
"routeId": "/support",
"mode": "agent",
"output": {
"messages": [
{
"content": "Use Forgot password on the sign-in screen to reset your password."
}
]
}
}
status tells me the route completed, routeId confirms which filesystem route ran, and mode says Dawn executed an agent descriptor.
The final state is under output; the example above trims it down to the assistant message I care about.
The model may call searchDocs and answer from the Passwords document, but the CLI output is the final JSON result, not a tool trace.
The exact answer text varies.
Now verify the app and start the local server:
npx dawn verify
npx dawn dev
dawn verify checks the application, discovers routes, regenerates types, checks dependencies, and catches runtime readiness problems.
dawn dev starts the local Agent Protocol server and reruns type generation as route files change.
If the entire application needs authentication or request policy, add app-level middleware at src/middleware.ts.
Where it grows
Suppose the support route starts handling refunds. I would add an order lookup tool, then have the route pause before it issues one. Dawn saves the LangGraph checkpoint in SQLite and asks a person to approve the action. When that decision arrives, the route resumes from the checkpoint. The same local thread can survive a dawn dev restart, although work that never reached a checkpoint is not recoverable. If the refund investigation takes several steps, plan.md gives it a persisted task list that the support interface can show as progress.
The project might grow into this:
src/app/support/
index.ts
plan.md
tools/
lookupOrder.ts
issueRefund.ts
skills/
refund-policy/
SKILL.md
subagents/
policy/
index.ts
workspace/
refund-notes.md
The two tools keep order lookup and the approved refund action separate. plan.md gives a long investigation a checklist. The refund skill holds instructions that the route can load when it needs the policy details. If the policy lookup deserves its own prompt and model, the child policy route gives that work a boundary. workspace/refund-notes.md gives the agent a file it can read and update through the workspace tools.
I would cover this exact flow with a recorded fixture: look up an order, reach the approval checkpoint, resume with an approval, then assert that issueRefund received the expected input. The prompt, tools, and saved state still run against the recording, so the test is useful without a live model call every time. After that passes, dawn dev runs the thread and resume flow locally, and the Node build packages the same application as server.mjs with a Dockerfile.
I have enjoyed building Dawn, and I am excited about b4. If you want to follow along, I will be sharing the work at b4.run and @b4dotrun. The name makes me smile, which is probably reason enough.