@b4run/sdk
The TypeScript SDK for authoring B4.run apps: the runtime functions, such as agent(), defineMemory() and defineMiddleware(), and the types for tools, routes and runtime contracts.
Use this when: You are authoring routes, tools, middleware, memory declarations, or typed runtime contracts.
▶ Watch the 50-second navlog demo
Install
Requires Node.js 24 or later. create-b4-app usually installs the SDK for you, alongside @b4run/cli, because routes run on the CLI's runtime. To add both to an existing app:
npm install @b4run/sdk @b4run/cli
Add zod when declaring typed long-term memory:
npm install zod
Example
Define an agent route whose issueRefund tool needs a person's approval:
// src/app/support/index.ts
import { agent } from "@b4run/sdk"
export default agent({
model: "gpt-5-mini",
systemPrompt: "Answer support questions clearly and concisely.",
tools: { approve: ["issueRefund"] },
})
// src/app/support/tools/issueRefund.ts
/** Refund an order. */
export default async (input: { readonly orderId: string }) => {
return { refunded: input.orderId }
}
Each tool is a file with one default export: route-local tools live in the route's tools/ directory and shared tools in src/tools/. A name in tools.approve pauses each call until a person answers, and an "always" answer stops the prompts for that tool. Write { tool: "issueRefund", allowAlways: false } instead to ask on every call.
The same package provides adjacent memory and middleware declarations:
// src/app/support/memory.ts
import { defineMemory } from "@b4run/sdk"
import { z } from "zod"
export default defineMemory({
kind: "semantic",
scope: ["workspace", "route"],
schema: z.object({
subject: z.string(),
predicate: z.string(),
value: z.string(),
}),
})
// src/middleware.ts
import { defineMiddleware } from "@b4run/sdk"
export default defineMiddleware(() => ({ action: "continue" }))
Alternatively, middleware that owns a long-lived resource, such as a database
pool, uses the lifecycle form instead of the function above. setup runs once,
lazily, before the first gated request (and is retried on the next request if
it fails); dispose runs when the Node runtime shuts down.
// src/middleware.ts
import { allow, defineMiddleware, reject } from "@b4run/sdk"
import { Pool } from "pg"
let pool: Pool | undefined
export default defineMiddleware({
async setup() {
pool = new Pool({ connectionString: process.env.DATABASE_URL })
await pool.query("select 1")
},
async dispose() {
await pool?.end()
},
async handle(req) {
const token = req.headers.authorization?.replace(/^Bearer /, "")
if (!token) return reject(401)
const result = await pool?.query("select user_id from sessions where token = $1", [token])
const row = result?.rows[0]
return row ? allow({ userId: row.user_id }) : reject(401)
},
})
The auth middleware recipe walks through bearer-token verification. The starter templates resolve the caller once, in src/auth.ts, and import it from both src/middleware.ts and src/thread-access.ts; keep that shape when you add thread access, so both files agree on who the caller is.
Runtime and stability
@b4run/sdkis the supported, edge-safe application surface.@b4run/sdk/pureis a supported, dependency-free edge-safe integration surface.@b4run/sdk/testingis a supported Node-only scenario-authoring surface. It is separate from@b4run/testing.
Related
@b4run/clidevelops, tests, builds, and serves routes declared with the SDK.@b4run/testingprovides programmatic harnesses for B4.run applications.- SDK API reference
- Agents
- Tools
- Middleware
- Memory
- Scenario testing
Maturity and support
B4.run is pre-1.0, and its public surface can change. All publishable B4.run packages release together as a fixed group; review the @b4run/sdk changelog and upgrading guide before upgrading. For support, use GitHub Discussions; report defects in GitHub Issues.
License
MIT. See the repository license.