Pre-release. HareBox is a working local build with no public service yet. Statuses on every feature say what runs today; speed and reliability figures are targets until measured. Status

Parallel agent fleets

Prepare one environment, fork it into many exact copies, and give each copy its own task. Keep the winner, delete the rest.

For Teams clearing a backlog, running research sweeps or trying several approaches at once

Why it pays

  • Setup once, not N times Install, build and warm caches once. Forks copy the prepared machine, running processes included.
  • Truly independent workers Each fork has its own disk, processes, IP and identity, so agents can't collide.
  • Compare and keep the winner Tag forks with a label, watch them finish on one event stream, and see the cost of each branch.

Step by step

  1. Prepare a base machine

    Clone the repo, install dependencies and start the dev server and test watcher once.

    CLI

    harebox new --name base --label experiment=auth
    harebox exec current -- ./setup-and-warm-caches.sh

    Python

    base = hb.create(name="base", labels={"experiment": "auth"})
    base.run("./setup-and-warm-caches.sh")

    TypeScript

    const base = await hb.create({ name: "base", labels: { experiment: "auth" } });
    await base.run("./setup-and-warm-caches.sh");
  2. Fork it into several exact copies

    Each copy starts from the same moment, with the server already running.

    CLI

    harebox fork current --count 4 --label experiment=auth

    Python

    forks = base.fork(count=4, labels={"experiment": "auth"})

    TypeScript

    const forks = await base.fork({ count: 4, labels: { experiment: "auth" } });
  3. Give every fork its own task

    Send each fork a different issue, or the same task with a different harness, and let them run.

    CLI

    issue=101
    for id in $(harebox list --label experiment=auth --json | jq -r '.sandboxes[].id'); do
      harebox prompt "$id" "Fix issue $issue, run the tests, open a PR" --harness claude --budget 2 --detach
      issue=$((issue + 1))
    done

    Python

    issues = [101, 102, 103, 104]
    for box, issue in zip(forks, issues):
        hb.create_prompt(box.id, body={"prompt": f"Fix issue {issue}, run the tests, open a PR",
                                       "harness": "claude", "budget_usd": 2})

    TypeScript

    const issues = [101, 102, 103, 104];
    await Promise.all(forks.map((box, i) => hb.createPrompt(box.id, {
      body: { prompt: `Fix issue ${issues[i]}, run the tests, open a PR`, harness: "claude", budget_usd: 2 },
    })));
  4. Watch them finish on one stream

    No polling: follow the account's events and react as each turn ends.

    CLI

    harebox events --type "prompt.finished" --follow

    Python

    for ev in hb.stream_org_events(query={"type": "prompt.finished"}):
        print(ev["sandbox"], ev["data"])

    TypeScript

    for await (const ev of await hb.streamOrgEvents({ query: { type: "prompt.finished" } })) {
      console.log(ev.sandbox, ev.data);
    }
  5. Compare cost, keep the winner

    See what each branch cost, keep the best result, and delete the rest.

    CLI

    harebox usage --all --group-by label
    harebox delete LOSING_ID

    Python

    for box in forks:
        print(box.id, hb.get_sandbox_usage(box.id)["total_usd"])

    TypeScript

    for (const box of forks) console.log(box.id, (await hb.getSandboxUsage(box.id)).total_usd);

Tips

  • A fork copies memory, so it copies session tokens too. Rotate tokens in a post-fork step if an app holds one.
  • Put a budget on the experiment's label before you start, so the whole fleet has one ceiling.
  • Use --count rather than a loop: one call is one start-rate check and one idempotent request.
  • Forks of a stopped sandbox start from its disk; forks of a running one copy memory too.