Aex Brain
Customize and operate

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.mjs

It 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:8080

These 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.

On this page