Write an environment
Connect your own runtime or workspace so Brain can run tools there.
An environment lets Brain run code in a service you control, such as a sandbox or browser. You usually need only an existing environment. Write one when you need a different runtime or resource lifecycle.
This guide assumes you can run an HTTP service and control its deployment. For a request handler that only returns a tool result, use the existing HTTP tool extension.
Run the JavaScript example
The lazy workspace example is a small HTTP service that echoes tool input and keeps a shared in-memory workspace. With Node.js 22 or newer:
git clone https://github.com/aexhq/brain.git
cd brain
node examples/lazy-environment.mjsIt listens on 127.0.0.1:8090. For this local example, run Brain on the same host using the
source build:
BRAIN_DATA_DIR="$PWD/brain-data" \
BRAIN_API_TOKEN=quickstart \
BRAIN_ENV_WORKER="$PWD/target/debug/brain-env-worker" \
./target/debug/brain --listen 127.0.0.1:8080These are Bash commands. On Windows use PowerShell environment assignments and the .exe
binaries. A Docker-hosted Brain cannot reach this service through its own 127.0.0.1.
Connect a tool
In the quickstart app, import environment and replace the order tool with:
import { environment } from "@aexhq/brain";
const workspace = environment({ url: () => "http://127.0.0.1:8090" });
const echo = tool({
name: "echo",
description: "Echo a message in the workspace.",
input: z.object({ message: z.string() }),
implementation: { type: "reference_echo" },
});
// In sessions.create:
// tools: [echo({ env: workspace({ name: "workspace" }) })],Send "Call echo with the message hello." and run the app. The transcript contains the echoed
input and a workspace entry count. JavaScript needs no build step; use your normal compiler
for a TypeScript service.
Edit the example's execute handler to run your own fixed operation. It calls the supplied
finish callback before returning a receipt, so Brain knows the tool has completed.
Add typed methods and observations
Use environmentHandler to validate provider options and named method inputs like a Tool.
The service authenticates Brain's request, then passes the decoded command to handler.run().
This excerpt assumes provider is your integration with attach, health, release and resource-loss
callbacks; it replaces lifecycle handling inside your service:
import { environmentHandler } from "@aexhq/brain";
import { z } from "zod";
const handler = environmentHandler({
options: z.object({ region: z.string() }),
async setup(ctx) {
await provider.attach(ctx.sessionId, ctx.environment.name, ctx.options.region);
const reporter = ctx.reporter;
provider.onResourceLost(ctx.environment.name, async resource => {
await reporter.emitResult({
scope: "resource", resource, code: "lost", message: "The resource is no longer available.",
});
});
},
methods: {
inspect: {
description: "Read current provider health.",
input: z.object({ resource: z.string() }),
run: (input, ctx) => provider.inspect(ctx.environment.name, input.resource),
},
},
teardown: ctx => provider.release(ctx.environment.name),
});Give the provider's watchers only ctx.reporter; keep operation
services in the handler. The reporter can outlive setup and carries no control authority.
An operation can call another authorized environment through ctx.environments. Its grants
are separate from those of any Tool or Agentloop it hosts. Export handler.methods on the
environment(...) factory so the application can grant the methods it chooses.
Use effect: "replace" for a method that replaces the entire execution environment. Manage
resources inside it with ordinary methods. Brain handles references, grants, journaling and
activation; your Env implements the provider behavior. See
Control environments.
Python and other languages
An environment service uses HTTP and JSON, so it can be written in any language with an HTTP
server. Brain includes tested JavaScript services and a
Python project runner.
The runner itself is JavaScript; it prepares and runs Python tools using uv and their lockfiles.
For Python code that only needs computation and Brain callbacks, use the smaller packaged Python tool example instead. For other service languages, implement the Environment protocol; Brain does not supply a separate environment SDK for each language.
Deploy and use it
Deploy the service where your Brain server can reach it. Configure its URL and credential in
the environment(...) factory, then create a new session with that environment. Prepare code
and dependencies before executing the tool. Define when workspaces are kept and released.
The local example does not isolate untrusted code. A deployed service must authenticate requests, validate operations, and limit the code's access to files, secrets and the network. The protocol reference describes setup, execution, cancellation and cleanup.
For supported hosted integrations, see the Aex docs.