Quickstart
Run an agent that looks up an order and tells you whether it has shipped.
Give an AI model a function from your app and watch it answer a real question. You need Docker, Node.js 22 or newer and an OpenAI API key; OpenAI bills model calls to your key. Rather not run a server? Follow the Aex quickstart instead.
1. Start Brain
docker run --rm -p 127.0.0.1:8080:8080 \
-e BRAIN_LISTEN=0.0.0.0:8080 -e BRAIN_API_TOKEN=quickstart \
-e BRAIN_AGENTLOOPS='{"pi":{"command":"brain-agentloop-pi"}}' \
-v brain-data:/var/lib/brain ghcr.io/aexhq/brain-agentloops:latestLeave this terminal running. The volume keeps saved sessions across restarts. The token is for local use only; read Operate Brain before exposing a server. Commands use Bash. In PowerShell, join multiline commands onto one line.
2. Install the client
In another terminal:
mkdir brain-example
cd brain-example
npm init -y
npm pkg set type=module
npm install @aexhq/brain@0.41.0 zod@4.4.3Add the compiler and runner:
npm install --save-dev typescript@5.9.2 tsx@4 @types/node@22Set your model key in the same terminal:
export OPENAI_API_KEY="your-key"In PowerShell, use $env:OPENAI_API_KEY = "your-key". Never commit keys to source control.
3. Add a tool and send a message
Save this as order.ts. It's the complete order example,
and it runs unchanged as JavaScript. The Zod input schema gives the tool its argument types.
import { Brain, tool } from "@aexhq/brain";
import { z } from "zod";
const apiKey = process.env.OPENAI_API_KEY;
if (!apiKey) throw new Error("OPENAI_API_KEY is required");
const lookupOrder = tool({
name: "lookup_order",
description: "Look up an order by id.",
input: z.object({ id: z.string() }),
run: ({ id }, ctx) => ctx.finish({ id, status: "shipped" }),
});
const brain = new Brain({ baseUrl: "http://127.0.0.1:8080", token: "quickstart" });
brain.sessions.create({
agentloop: { name: "pi" },
model: { provider: "openai", name: "gpt-5-mini", apiKey },
tools: [lookupOrder()],
}).then(async session => {
const after = session.state.lastSequence;
await session.send("Look up order A-1001. Has it shipped?");
for await (const event of session.events(after)) {
if (event.type === "turn_failed") throw new Error(JSON.stringify(event.data));
}
console.log(JSON.stringify(await session.transcript(), null, 2));
console.log("Session:", session.id);
}).catch(error => {
console.error(error);
process.exitCode = 1;
});4. Run it
npx tsc --noEmit --strict --module NodeNext --target ES2023 order.ts
npx tsx order.tsYou'll see a lookup_order result in the transcript, followed by an answer saying order A-1001
has shipped. Exact wording varies by model. Now swap the sample return value for a real lookup in
your backend.
What just happened
- The agentloop ran on Brain. Startup registered Pi; the session selected it by name. Pi owns the agent loop and compaction; Brain executes and journals its model and tool calls.
- The tool ran in your app.
lookup_orderexecuted in this Node.js process, andctx.finish(value)returned its result and completed the call. - The script exited cleanly. After five idle seconds the client released its tool connection. The session and its history are still on the server.
To keep chatting, add another send() in the callback. A later send through the same live client
reconnects before it starts work.
If other callers or future autonomous events need these tools, pass
connectionIdleTimeoutMs: 0 to new Brain(...). Event subscriptions stay open until you stop them.
brain.close() permanently disposes of the client, session.end() finishes the conversation and
session.delete() removes its history. See Sessions.
Next steps
- Sessions: stream output, collect outcomes and reconnect.
- Write a tool: connect your application or publish an extension.
- Agent loops: change how the agent works.
- Operate Brain: deploy, back up and upgrade your server.