Skip to content
All pages

Running agents

Start Claude Code, Codex or opencode in a moatbox space, follow the run, steer it, continue it, and watch the agent's browser screen.

moatbox runs coding agents inside spaces: Firecracker microVMs with their own disk, on moatbox servers or on a server your team links. A run is one agent working on one task in one space. This page covers starting a run from the dashboard or the API, following it, steering it while it works, continuing it after it ends, and watching the agent's browser on a live screen. To create a space first, see Spaces.

Supported agents

agent value Dashboard label
claude Claude Code
codex Codex
opencode opencode

moatbox starts each run in a gVisor sandbox container inside your space. All agents share the space's /workspace directory, and each agent keeps its own /home/agent. The agent CLI runs with its own permission prompts turned off (--dangerously-skip-permissions for Claude Code, --dangerously-bypass-approvals-and-sandbox for Codex, --auto for opencode). moatbox's gateway decides which actions wait for a person instead; see Approvals.

An agent needs a model account before it can do any work: a subscription sign-in inside the space, or provider keys from a team AI profile. See Signing agents in.

Start a run from the dashboard

  1. Open the space. Its Agents page lists the space's runs.
  2. Click New agent.
  3. Type the task in the box ("What should the agent do? Drop files here to attach them.").
  4. Adjust the settings in the bar under the box, if you need to.
  5. Click Start or press Enter. Shift+Enter adds a new line.

The dashboard opens the run's page once the run has started.

The bar under the task box holds these settings:

  • Agent and model menu. The button reads like Claude Code / Default. Pick the agent on the left (Claude Code is preselected), then a model. Default leaves the choice to the agent's own configuration. For a model the list doesn't show, type its id in the field at the bottom and press Enter (opencode takes provider/model). An agent that isn't signed in carries a sign in tag.
  • Effort. A slider in the same menu with the levels the chosen model offers. default keeps the agent's own setting.
  • Fast mode. A switch, shown for models that have one. Only Claude Code and Codex support it.
  • Working directory. The button that shows /workspace. Click it to browse folders and pick where the agent starts. If the space is asleep, the dashboard can't list folders, so you type a path under /workspace.
  • Screen. Gives the agent a browser on a screen you can watch and take over. The button reads Screen on when set. See Watch the agent's screen.
  • Branch. Optional. The placeholder shows the branch the workspace is on.
  • + attaches files. You can also drop or paste files into the box. The dashboard uploads each file to /home/agent/uploads/ in the space (2 GiB at most per file) and appends an "Attached files:" list with their paths to your task.

Working directory and branch

The working directory must be /workspace or an existing folder under it, with no .. in the path. It defaults to /workspace.

When you set a branch, moatbox switches the git checkout in the working directory to that branch before the agent starts, and creates the branch if it doesn't exist. moatbox runs the switch with git hooks turned off. Without a branch, the run records the branch the checkout is on.

moatbox refuses a branch while another run of the same agent is live in the space, because the switch would move that run's checkout. Wait for that run to end or stop it.

Sleeping and blocked spaces

Starting a run wakes a sleeping space. If the space takes longer than about 20 seconds to wake, the API answers 503 with "the space is waking up; start the run again in a minute" and a Retry-After: 15 header.

A space that is suspended (423), still being set up (409), or read-only because its plan isn't active (402) refuses new runs. The dashboard shows New agent only on an active space.

Start a run from the API

Send POST /api/v1/spaces/{space}/runs with an API key that has the run action (see API keys).

curl -X POST https://app.moatbox.dev/api/v1/spaces/my-space/runs \
  -H "Authorization: Bearer $MOATBOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"agent": "claude", "task": "fix the failing test", "branch": "fix-test", "effort": "high"}'

moatbox answers 202 Accepted with the run id:

{"id": "3f9a1c2e"}
Field Required Meaning
agent yes claude, codex or opencode.
task yes The prompt.
model no Model id, passed to the agent CLI. Left out, the agent uses its own default. Up to 100 characters: letters, digits, ., _, :, /, @, -, starting with a letter or digit.
effort no Up to 16 lowercase letters. moatbox passes it as --effort to Claude Code, model_reasoning_effort to Codex, and --variant to opencode.
speed no fast, or leave it out. Claude Code and Codex only.
cwd no Working directory. Defaults to /workspace.
branch no Git branch to switch to, created when missing.
desktop no true gives the agent a browser on a watchable screen.
resume no Id of an ended run to continue. See Continue a run.
env no Environment variables for this run: at most 50, each value at most 32 KiB. moatbox refuses names it reserves.
broker no Integrations the run may use, such as ["github"]. The gateway gives the agent a placeholder and puts the real key on requests to that service. See Integrations.
hold no MCP tool name patterns to hold for approval, such as ["linear.*"]. See Approvals.
allow no Extra hosts the run may reach, added to the defaults.
ai_profile no Name or id of a team AI profile. The run's model keys then go through the moatbox AI gateway.

A request without agent, or without task, gets 400 with "agent and task are required". A sign-in run takes no task; see Signing agents in.

The moatbox MCP server offers these operations as the start_run, list_runs, get_run, get_run_timeline, send_message and stop_run tools. Its start_run takes only agent, task, model, effort, cwd and branch; use the API for the other fields. See MCP server.

Follow a run

In the dashboard

The run page header shows the model, the working directory, the branch with a marker for "Uncommitted changes" or "Clean", and token usage. Info expands the details: who launched it, start and end times, the image and the run id.

  • Conversation shows the agent's messages and tool calls as they arrive.
  • Log shows the agent's raw output lines, with Follow, Expand all and Copy.
  • The Workspace panel has three tabs: Files, Changes (changed files in the workspace), and Terminal (the commands the agent ran). On a narrow screen, Workspace is a tab next to the other two.

The Agents page lists runs with their task, directory and branch, who launched them, status, duration and start time. Filter by All, Waiting, Running, Failed or Done. The status badge reads Running, Waiting for you (an action is held for approval, see Approvals), Done (exit code 0) or Failed (any other exit code). SSH sessions and commands, which the gateway records, show under an SSH tab when there are any; see Connect to a space.

The Live page lists running agents across all your spaces. Each row shows Working, Waiting on approval or Box asleep, the latest event, and a Stop button. A waiting run gets a Review link; an asleep one gets Wake.

When a run ends, moatbox compares the workspace with its state at the start and records the change on the timeline as a diff event. The patch stops at 1 MiB.

From the API

curl https://app.moatbox.dev/api/v1/spaces/my-space/runs?limit=20 \
  -H "Authorization: Bearer $MOATBOX_API_KEY"
Request Returns
GET /api/v1/spaces/{space}/runs {"runs": [...]}, newest first. limit takes 1 to 200 (default 50); before takes a run id.
GET /api/v1/spaces/{space}/runs/{run} One run.
GET /api/v1/spaces/{space}/runs/{run}/timeline {"run": {...}, "events": [...]}
GET /api/v1/spaces/{space}/stream Server-sent events for the whole space. Resume with ?after=<event id> or the Last-Event-ID header. Filter on run_id yourself.

A run object carries id, agent, task, launched_by, started, ended (empty while it runs), exit, model, effort, speed, cwd, branch, parent (the run it continues) and session (the agent's own session id). Agent sign-ins appear in the list too, with "kind": "login".

Each event has id, run_id, ts, kind, summary, text, and evidence: observed when moatbox saw it happen, reported when it comes from the agent's own output. These kinds mark a run's life:

kind Meaning
run.start The run started.
user.message Someone sent the agent a message. The summary starts with who.
run.interrupt Someone interrupted the agent.
run.stop Someone stopped the run.
diff The workspace changes, recorded when the run ends.
run.error moatbox hit an error running the agent.
run.end The run ended. The summary reads exit <code>.

For webhooks on these events, see Events, webhooks and telemetry.

Steer a running agent

Action Dashboard API Agents
Send a message Box under the conversation, Send POST .../runs/{run}/messages with {"text": "..."} Claude Code
Interrupt Interrupt, or Esc POST .../runs/{run}/interrupt Claude Code
Stop Stop, or ⌘. or Ctrl+. POST .../runs/{run}/stop All

Send a message

While a Claude Code run works, type in the box at the bottom ("Message the agent while it works") and click Send. moatbox writes the message into the agent's session and records it as a user.message event with your name.

curl -X POST https://app.moatbox.dev/api/v1/spaces/my-space/runs/3f9a1c2e/messages \
  -H "Authorization: Bearer $MOATBOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text": "also update the changelog"}'

Codex and opencode run in one-shot mode and read no messages while they work. The dashboard says "You can reply once this run ends", and the API answers 409. Once Claude Code has answered every message and is finishing, a new message also gets 409 ("reply to start a follow-up run"). Continue the run instead.

Interrupt

Interrupt (or Esc, when you aren't typing in a non-empty field) stops Claude Code's current step. The run stays open, its status shows Waiting for you, and the agent waits for your next message. For Codex and opencode, the API answers 409: moatbox has no control channel into those agents, so you stop the run and continue it.

Stop

Stop (or ⌘. or Ctrl+.) ends the run for any agent. The Live page has a Stop button per run too. moatbox records run.stop with your name, stops the sandbox, saves the workspace diff and ends the run with the agent's exit code. Stopping a run that has already ended gets 409.

After a run ends

The run page swaps the controls for Changes, Terminal, Retry and Fork, and the box at the bottom turns into Continue.

Continue a run

Type a reply in the box ("Reply to continue in a new run that resumes this session") and click Continue. You can change the model, effort and fast mode in the same box. moatbox starts a new run that resumes the agent's own session: --resume for Claude Code, codex exec resume for Codex, --session for opencode. The new run's page shows "Follows" with the earlier run's id, and the earlier run shows "Continued in".

From the API, start a run with resume set to the earlier run's id and your reply as task:

curl -X POST https://app.moatbox.dev/api/v1/spaces/my-space/runs \
  -H "Authorization: Bearer $MOATBOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"agent": "claude", "task": "now add a test for the edge case", "resume": "3f9a1c2e"}'

The new run keeps the earlier run's working directory, model, effort and speed unless you set them. moatbox refuses to continue when:

  • the agent differs from the earlier run's agent,
  • the earlier run is still running (send it a message instead),
  • the earlier run reported no agent session. The dashboard then says "This run reported no agent session, so it can't be continued".

Retry, Terminal, Changes, Fork

  • Retry starts a new run with the same agent, task, model, effort, fast mode, directory and branch, in a fresh session.
  • Terminal offers an SSH command for a shell in the space. Use ssh moatbox-<space>, the host that moatbox connect writes to your SSH config; see Connect to a space.
  • Changes opens the changed files in the Workspace panel.
  • Fork copies the space into a new one. Only team owners and admins can fork.

Watch the agent's screen

Turn on Screen when you start the run, or send "desktop": true to the API. moatbox starts a Chromium browser on a virtual desktop beside the agent's sandbox and gives the agent a browser MCP server to drive it. The browser's page loads leave through the space's gateway, like the agent's own traffic. moatbox records each browser action with a screenshot and redacts text typed into password-like fields. It holds navigations and clicks whose URL or target mentions words such as pay, checkout, password, send, delete, publish or deploy, so a person can approve them.

The run page shows a Screen section with the live desktop:

Status line Meaning
"Watching. Only the agent acts." You see the screen; the agent drives.
"You have control. The agent's browser waits." You drive the browser; the agent's browser actions wait.
"(teammate's name) has control. The agent's browser waits." A teammate drives the browser.
"The run ended. The screen is closed." The run is over.
  • Take control lets you use the mouse and keyboard. One person holds control at a time, and screens narrower than 768 pixels get a view-only screen.
  • Hand back returns control to the agent. Control also lapses after 5 minutes without input from you.
  • Hide and Show collapse and restore the screen. Reconnect appears if the connection drops and the automatic retries fail.

moatbox records on the run's timeline each viewer who connects and how long they stayed, and takes a screenshot every 5 seconds while someone watches. Opening the screen, taking control and handing it back go into the space's audit log.

The screen opens only from the dashboard in a signed-in browser. An API key can read GET /api/v1/spaces/{space}/runs/{run}/desktop, which returns {"live": false} when the run has no screen, but it can't take control or open the screen stream.