Quickstart
Run Brain and give a session a Tool from your own process.
Run a server
docker run --rm -p 127.0.0.1:8080:8080 \
-e BRAIN_LISTEN=0.0.0.0:8080 -e BRAIN_API_TOKEN=quickstart \
-v brain-data:/var/lib/brain ghcr.io/aexhq/brain:latestOr build both executables:
cargo build --release -p brain-server --bin brain -p brain-env-worker --bin brain-env-worker
BRAIN_DATA_DIR="$PWD/brain-data" \
BRAIN_ENV_WORKER="$PWD/target/release/brain-env-worker" \
./target/release/brain --listen 127.0.0.1:8080Brain permits an unauthenticated loopback listener. A non-loopback listener requires
BRAIN_API_TOKEN.
Create a session
Install the client, an Agentloop, and Zod:
npm install @aexhq/brain@0.24.3 @aexhq/agentloop-pi@6.1.2 zodKeep the Brain SDK version aligned with the version required by your extensions.
Save this as order.mjs:
import { Brain, brainEnv, hostEnv, tool } from "@aexhq/brain";
import { pi } from "@aexhq/agentloop-pi";
import { z } from "zod";
const orders = { "A-1001": { status: "shipped", eta: "Thursday" } };
// `run` is a function this process holds. Placing the Tool in `hostEnv` says so.
const lookupOrder = tool({
name: "lookup_order",
description: "Look up an order's status by id.",
input: z.object({ id: z.string() }),
run: async ({ id }, ctx) => {
await ctx.emit("order_lookup_started", { id });
return orders[id] ?? { status: "unknown order" };
},
});
const brain = new Brain({
baseUrl: "http://127.0.0.1:8080",
token: "quickstart",
});
try {
const session = await brain.sessions.create({
model: {
provider: "openai",
name: "gpt-5-mini",
apiKey: process.env.OPENAI_API_KEY,
},
agentloop: pi({ env: brainEnv({ name: "brain" }) }),
tools: [lookupOrder({ env: hostEnv({ name: "app" }) })],
});
await session.send("Where is order A-1001?");
for await (const event of session.events()) {
console.log(event.sequence, event.type, event.data);
}
await session.end();
await session.delete();
} finally {
await brain.close();
}The SDK registers this process as a host and keeps its command connection open until brain.close(). Brain commits
tool_call_started before sending the command. ctx.emit appends an extension event to the same
journal before its promise resolves, and the Tool result is committed before the Agentloop receives
it. Brain sends each effect once; it does not retry a Tool when the outcome is uncertain.
For a custom Agentloop, wrap already-built Component bytes and place the factory explicitly:
import { agentloop, brainEnv, component } from "@aexhq/brain";
const custom = agentloop({
implementation: component(new URL("./agentloop.wasm", import.meta.url)),
});
const placed = custom({ env: brainEnv({ name: "brain" }) });Brain admits the raw Component. Compilation and source-language tooling stay outside Brain.
Build without an extension package
The repository includes a small Rust reference Agentloop
and a lazy Environment.
They depend only on Brain's public contracts. Build the loop with
cargo build --manifest-path examples/reference-agentloop/Cargo.toml --target wasm32-wasip2 --release,
then explicitly admit the Component before creating sessions. See Write an Agentloop
and Write an Environment for the host calls and the Environment protocol.