Aex Brain
Guides

Spawn a subagent

A tool in your own process creates a child session and returns its answer.

A Tool with a run function, placed in the host env, stays in the process that created the session, so it can use the SDK. That is enough for a subagent today: the tool creates a child session, sends it the task, reads the reply from the child's event feed, and returns it as the tool's output. The parent's loop sees an ordinary tool result. Native parent and child links between sessions are on the roadmap.

import { hostEnv, tool } from "@aexhq/brain";
import { z } from "zod";
import { coder } from "./loops.js";
import { sandbox } from "./environments.js";
import { test } from "./tools.js";

const delegate = tool({
  name: "delegate",
  description: "Hand a subtask to a fresh agent and return its answer.",
  input: z.object({ task: z.string() }),
  run: async ({ task }) => {
    const childEnv = sandbox({ name: "sandbox", image: "node:22" });
    const child = await brain.sessions.create({
      model,
      agentloop: coder({ env: childEnv }),
      tools: [test({ env: childEnv })],
    });
    await child.send(task);
    let answer;
    for await (const event of child.events()) {
      if (event.type === "output_emitted" && event.data.type === "assistant_message") {
        answer = event.data.message;
        break;
      }
    }
    await child.end();
    return { answer };
  },
});

The child gets its own Agentloop, model, Tools, Environments, and canonical journal. Calling the Environment factory again gives the child its own Environment.

Put it in a session

The parent runs the same loop with the sandbox tools plus delegate. Brain sends the delegate call to this process over the host's command stream, the function above answers it, and the child's whole run is journalled under its own session id.

const box = sandbox({ name: "sandbox", image: "node:22" });
const session = await brain.sessions.create({
  model,
  agentloop: coder({ env: box }),
  tools: [test({ env: box }), bash({ env: box }), delegate({ env: hostEnv({ name: "app" }) })],
});

Every event the child produces is readable on its own session, so a trace of the parent and a trace of the child are the same kind of journal projection. See Write a Tool for the host path used by a run Tool.

On this page