Manual contents

Run several agents on one machine

This guide sets up one machine from an environment and runs three agents on it at once, a claude, a codex and a gemini, each in a folder of its own. They share the checkout, the installed packages and the disk, the way three terminals on one laptop do. The machine is a workspace.

The three agents can read and change each other's work

Conversations in one workspace run as one user on one disk. Each can read the others' files, configuration and tokens, and can leave instructions or hooks that the others then follow. Put agents in one workspace only when you would let them share a laptop. For isolation, launch without --workspace, and each conversation gets a sandbox of its own.

Before you start

  • The CLI, signed in with managoat auth login. A workspace needs a full-scope key, so the MANAGOAT_TOKEN inside a sandbox will not do.
  • An inference credential for each provider you use, added under Account → Inference keys: Anthropic for claude, OpenAI for codex, Google for gemini.
  • A repository to work on. A private one needs a token in a vault, as the guided tour shows.

1. Describe the machine and the agents

Write one manifest with the environment and the three agents. A conversation's working directory must lie under /home/sprite or under one of the environment's repository mounts, so the checkout can live at either. This guide clones it to /home/sprite/app.

---
apiVersion: managoat/v1
kind: Environment
metadata:
name: app
spec:
repositories:
- url: https://github.com/you/your-app
mount_path: /home/sprite/app
secret_key: GITHUB_TOKEN
setup_script: cd /home/sprite/app && npm ci
---
apiVersion: managoat/v1
kind: Agent
metadata:
name: web-dev
spec:
runtime: claude
model: anthropic/claude-sonnet-5
---
apiVersion: managoat/v1
kind: Agent
metadata:
name: api-dev
spec:
runtime: codex
model: openai/gpt-6-astra
---
apiVersion: managoat/v1
kind: Agent
metadata:
name: writer
spec:
runtime: gemini
model: google/gemini-3.1-pro-preview

Apply it, and put the token the clone reads in a vault:

managoat apply -f app.yml
managoat vault create github
managoat vault set-secret github GITHUB_TOKEN "$GITHUB_TOKEN"

The agents name no environment: in a workspace the environment comes from the workspace. An agent with allowed_environment_ids or allowed_vault_ids must allow the workspace's.

Give the runtimes their settings

The setup script runs before any agent arrives, so it cannot write into an agent's ~/.claude or ~/.codex: each conversation gets a config home of its own, made later. It writes into the runtime template instead, $MANAGOAT_RUNTIME_TEMPLATE, whose claude/, codex/ and gemini/ directories hold what ~/.claude, ~/.codex and ~/.gemini would. Each conversation's config home starts as a copy of its runtime's directory.

This setup script gives every claude agent a hook that formats the files it edits, and every codex agent a larger limit for the AGENTS.md files it reads. Replace the environment's setup_script in app.yml with it:

setup_script: |
cd /home/sprite/app && npm ci
t="$MANAGOAT_RUNTIME_TEMPLATE"
# Claude: after each Edit or Write, format what changed.
mkdir -p "$t/claude/hooks"
cat > "$t/claude/hooks/format.sh" <<'EOF'
#!/bin/sh
cd /home/sprite/app && npx --no-install prettier --write --log-level warn . >/dev/null 2>&1
exit 0
EOF
chmod +x "$t/claude/hooks/format.sh"
cat > "$t/claude/settings.json" <<'EOF'
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "\"$CLAUDE_CONFIG_DIR\"/hooks/format.sh" }
]
}
]
}
}
EOF
# Codex: read AGENTS.md files up to 64 KiB.
cat > "$t/codex/config.toml" <<'EOF'
project_doc_max_bytes = 65536
EOF

The hook's command names the script through $CLAUDE_CONFIG_DIR. That is the conversation's own config home, where the script was copied, so every claude conversation runs its own copy.

Some things to know:

  • Managoat merges its own keys into Claude's settings.json and keeps the rest, so the hooks survive. It replaces mcpServers with the agent's own: put MCP servers on the agent.
  • The template is copied when a conversation's config home is made. Editing it later changes only conversations started after the edit.
  • Do not put secrets in the template. Every conversation gets a copy, and the workspace's disk reads can read it.

Configure the runtimes from the setup has the full rules.

2. Create the workspace

managoat workspace create app-box --environment app --vault github --wait
workspace + app-box (0198c9a2-…) provisioning
▸ workspace 0198c9a2-…: provisioning
▸ workspace 0198c9a2-…: ready

Managoat builds the machine now, before any agent arrives: it clones the repository, installs the packages and runs the setup script once. --wait follows the setup and exits non-zero if it fails, with the failing step and the end of what it printed. Without --wait, the command returns at once and managoat workspace wait app-box waits later.

The workspace always uses this environment, vault and provider; to use others, create another workspace. Editing the environment does reach it, at its next setup: a retry after a failure, or a rebuild after a loss.

3. Start the three agents, each in its own folder

--workspace puts the conversation on the workspace's machine, and --cwd picks the folder its agent works in. A missing folder is created.

managoat run web-dev --workspace app-box --cwd /home/sprite/app/web \
-p "Add a dark mode toggle to the settings page" > web.log 2>&1 &
managoat run api-dev --workspace app-box --cwd /home/sprite/app/api \
-p "Add an endpoint that returns the user's theme preference" > api.log 2>&1 &
managoat run writer --workspace app-box --cwd /home/sprite/notes \
-p "Draft release notes for a dark mode feature in this folder" > notes.log 2>&1 &
wait

Each run streams its own turn into its log and exits when that turn ends. The first line of each log names its conversation. The three run at the same time, in three conversations, on one machine. Each has its own config home, so their skills, MCP servers and sessions do not collide, and two of them could even share one folder.

Leave out --environment and --vault: the workspace supplies both. Naming a different one is refused with workspace_fixes_environment.

4. Keep talking to them

Send a follow-up to any of them by its conversation id, the usual way:

managoat conv list
managoat conv prompt <conversation-id> -p "Now add tests for it"

When nobody prompts any of them for the idle timeout (60 minutes by default), the machine parks. The next prompt to any of the three wakes it, with the disk as it was and that agent's session intact.

5. Look at the shared disk

The workspace's disk reads see everything under /home/sprite, every live conversation's working directory, and the environment's repository mounts:

WORKSPACE_ID=$(managoat workspace show app-box --json | jq -r .id)
curl --fail-with-body -H "Authorization: Bearer $MANAGOAT_API_KEY" \
"$MANAGOAT_URL/api/workspaces/$WORKSPACE_ID/git-status?path=/home/sprite/app"

That is one repository's status, with the changes all three agents made in it. A parked machine answers 409 sandbox_not_ready: a read never wakes it.

The same thing from the TypeScript SDK

import { Managoat } from "@managoat/sdk";
const managoat = new Managoat();
const env = await managoat.environments.get("app");
const vault = await managoat.vaults.get("github");
const ws = await managoat.workspaces.create({
name: "app-box",
environment_id: env.id,
vault_id: vault.id,
});
await managoat.workspaces.waitForReady(ws.id); // throws WorkspaceFailedError on a failed setup
const results = await Promise.all([
managoat.run("Add a dark mode toggle", { agent: "web-dev", workspace: ws.id, cwd: "/home/sprite/app/web" }),
managoat.run("Add a theme endpoint", { agent: "api-dev", workspace: ws.id, cwd: "/home/sprite/app/api" }),
managoat.run("Draft release notes", { agent: "writer", workspace: ws.id, cwd: "/home/sprite/notes" }),
]);
const status = await managoat.workspaces.gitStatus(ws.id, { path: "/home/sprite/app" });

From an editor

managoat acp takes the same two flags, so one editor entry per agent can open every session in one workspace, each in its own folder:

managoat acp --agent web-dev --workspace app-box --cwd /home/sprite/app/web

Read Connect an editor.

6. Clean up

A workspace's machine has no lifetime limit. It parks when idle, and even the operator's SANDBOX_MAX_LIFETIME_HOURS ceiling parks it rather than destroying it, so a parked machine stays at the provider until you delete the workspace. Delete it when you are done:

managoat workspace delete app-box

That terminates the three conversations, keeping their transcripts, and destroys the machine with its disk. Push the work first.

When it goes wrong

You see Why Do this
workspace setup failed: … The setup script, a package or a clone failed. The message names the step and quotes the end of what it printed. Read the rest with managoat workspace logs app-box. Fix the environment, then managoat workspace retry app-box --wait: that runs the setup again with the environment as it is now. A prompt into a conversation in the workspace does the same.
The setup succeeded, but the machine is not as the script should have left it The script did something you did not expect, and exited 0. managoat workspace logs <name> --step setup_script shows what it printed.
422 workspace_fixes_environment The launch named an environment or vault other than the workspace's. Drop --environment and --vault.
A conversation fails with runtime_template and not_a_json_object or unreadable A settings.json or .claude.json the setup script wrote into the template is not valid JSON, or a file in it is one the sandbox user cannot read (written with sudo). Fix the setup script, then create the workspace again: a ready workspace does not run its setup again.
An agent ignores a setting you added to the template The template is copied when a conversation's config home is made, so a conversation started before the change kept the old copy. Managoat also owns mcpServers and showThinkingSummaries. Start a new conversation. Put MCP servers on the agent.
422 environment_not_allowed or vault_not_allowed The agent's allowlist does not include the workspace's environment or vault. Add them to the agent's allowed_environment_ids or allowed_vault_ids.
403 with required_scope: "full" A sandbox's own key, such as MANAGOAT_TOKEN inside a sandbox, cannot use workspaces. Use a key from managoat auth login.
A prompt does not run, and its wake or reattach stage fails with sandbox_provider_disabled The instance no longer has the credentials of the workspace's sandbox provider. Without them Managoat cannot reach the machine, even one that was up. The machine and its disk are kept. Set the provider's credentials on the instance again, or create a workspace on another provider. The stage's advice says the same.
503 fleet_full on create Every sandbox slot on the provider is in use. Try again in a minute.
422 validation_failed on cwd The folder is not under /home/sprite (or /tmp for gemini) or a repository mount of the workspace's environment, or is inside /home/sprite/.managoat. Choose a folder under /home/sprite or under a repository's mount_path.
409 source_in_use_by_workspace deleting the environment or vault A workspace was created from it. Delete the workspace first.
A turn ends interrupted, machine_parked The operator's lifetime ceiling parked the machine. Prompt again; the machine wakes with its disk.

Where to go next