Quickstart
Start Brain on your machine, create a Pi session, and stream the reply with the TypeScript or Python SDK.
This guide walks you through running Brain on your machine and sending your first message. You start the server with the example host config, create a session that uses the Pi harness and an OpenAI model, and stream the reply with the TypeScript or Python SDK.
| Concept | In this guide |
|---|---|
| Host config | examples/local.json: one API token for the local tenant, the pi harness, the OpenAI provider, and no tools. |
| Session | One conversation that uses the pi harness and the openai/gpt-4.1-mini model. |
| Turn | The work Brain runs for your message: model calls until Pi finishes its reply. |
| Event | Records such as assistant.message and session.status that the example reads from the session's stream. |
Prerequisites
- Go 1.26 or newer to run the server.
- Node.js 24 or newer to run the Pi runner and JavaScript/TypeScript client.
- Python 3.11 or newer, only if you use the Python SDK or run the full test suite.
- An OpenAI API key. To use another provider, see Models & providers.
The SDKs install from npm and PyPI. This guide builds the server and Pi runner locally; platform examples use the published container image.
Get the code and build the Pi runner
Clone the repository and build the Pi runner. Brain starts shared runner hosts when sessions begin their first turn.
git clone https://github.com/aexhq/brain.git
cd brain
npm --prefix runners/pi ci --ignore-scripts
npm --prefix runners/pi run buildSet your keys and start Brain
Brain reads two secrets from the environment:
BRAIN_API_KEYis the token your application sends to Brain. It must be at least 24 bytes. Use a long random value anywhere other than your own machine.OPENAI_API_KEYis the model provider key that Brain passes to the Pi runner.
export BRAIN_API_KEY="replace-with-a-random-token-of-at-least-24-bytes"
export OPENAI_API_KEY="your-provider-key"
go run ./cmd/brain -config examples/local.jsonWhen the server is ready, it writes a JSON log line with "msg":"brain listening" to stderr. With examples/local.json, Brain listens on http://127.0.0.1:8080, writes local state to .brain/, grants BRAIN_API_KEY the local tenant with permission to manage sessions, registers the pi harness, and enables no tools.
Customize the config
Copy examples/local.json to brain.json to change settings without editing tracked files. brain.json is gitignored, and Brain loads it when you omit -config. Add -check to validate a config, including the environment variables it names, and exit without starting the server.
Open a second terminal
Leave the server running. Open a second terminal in the repository root and set the same BRAIN_API_KEY. Install the Python package if you use the Python example.
export BRAIN_API_KEY="replace-with-a-random-token-of-at-least-24-bytes"
python -m pip install aex-brain==2.0.0Run a session
Run the example script for your language. Install the published SDK for your language. If your system has no python command, use python3.
npm install --no-save --package-lock=false @aexhq/brain@2.0.0
node examples/session.mjs "Explain what makes a good agent tool."The script prints the session ID and the assistant's reply, then exits when the turn ends. Example output:
Session: 7d1c9e04b3a24f6e8c5a0b2d9e3f4a61
A good agent tool does one clearly named job, ...How the example works
Both scripts create a session, send one message, and read events until the turn for that message ends. Here are the same steps with comments:
import { Brain } from "@aexhq/brain";
const token = process.env.BRAIN_API_KEY;
if (!token) throw new Error("Set BRAIN_API_KEY before running this example");
// Connect with the token that the host config grants to the "local" tenant.
const brain = new Brain("http://127.0.0.1:8080", token);
// Create a session with the harness, model, and tools the agent should use.
const session = await brain.create({ harness: "pi", config: { model: "openai/gpt-4.1-mini", tools: [] } });
console.log(`Session: ${session.id}`);
// Send a message. The random key makes the request safe to retry.
const input = await brain.send(session.id, { type: "user.message", text: "Say hello.", key: crypto.randomUUID() });
// Read events until the turn for this message ends.
for await (const event of brain.observe(session.id)) {
const data = event.data as { text?: string; message?: string; status?: string };
if (event.type === "assistant.message") console.log(data.text);
if (event.input_id !== input.id) continue;
if (event.type === "session.error") throw new Error(data.message);
if (event.type === "session.status" && data.status === "idle") break;
}The scripts in examples/ take the prompt from their arguments. examples/session.mjs also reads a comma-separated list of tool names from BRAIN_TOOLS, and examples/session.py reads BRAIN_URL and BRAIN_MODEL. Ending the loop doesn't close the session; it stays available for more messages.
Call the HTTP API with cURL
The SDKs wrap Brain's HTTP API. You can make the same calls with cURL from a macOS or Linux shell.
Create a session. Brain returns 201 Created with the session; save its id.
curl http://127.0.0.1:8080/sessions \
-H "Authorization: Bearer $BRAIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"harness":"pi","config":{"model":"openai/gpt-4.1-mini","tools":[]}}'Send a message using the returned session id:
curl http://127.0.0.1:8080/sessions/SESSION_ID/events \
-H "Authorization: Bearer $BRAIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type":"user.message","text":"Hello","key":"first-message"}'POST /sessions/{id}/events returns 202 Accepted with the saved user.message event once the message is stored and queued. The event's id is the input_id on the events of the turn it starts.
Stream the session's events:
curl -N http://127.0.0.1:8080/sessions/SESSION_ID/events \
-H "Authorization: Bearer $BRAIN_API_KEY" \
-H "Accept: text/event-stream"The stream replays the session's retained events, then follows new events as they happen. Without Accept: text/event-stream, the same URL returns a JSON page of retained events. See Events & streaming for every event type.
What happened
- Brain loaded the host config. It checked that
BRAIN_API_KEYandOPENAI_API_KEYare set, registered thepiharness, and opened local state in.brain/. - The script created a session. Brain stored it with status
idleand returned201 Created. - The script sent a message. Brain saved a
user.messageevent, queued it, and returned202 Accepted. - Brain ran a turn. It emitted
session.statuswithrunning, started the Pi runner, and streamedassistant.deltaevents while the model wrote. The complete reply arrived asassistant.message. - The session went idle. A
session.statusevent withidleand the message'sinput_idended the turn. The session stays open for more messages until you close it.
Next steps
Add a tool
echo, inspect_session, and ask_approval.