> ## Documentation Index
> Fetch the complete documentation index at: https://piloop.autoresearch.inc/llms.txt
> Use this file to discover all available pages before exploring further.

# Runtimes

> Where an agent runs, and how piloop starts, watches, steers, shows and stops it.

A **runtime** is where an agent runs. It starts the agent, watches it until it ends, steers it with messages, shows
it to you, and stops it. piloop has two built-in runtimes. A Python package can [add more](#add-a-runtime).

A project sets its runtime with the `runtime` key of `piloop.toml`. A role can set another one with its own `runtime`
key. See [Configure a role](/concepts/roles#configure-a-role).

## What a runtime does for you

Every runtime has the same contract. These commands work with every runtime. The runtime of the agent defines what
each command does:

| You run | The runtime of the agent |
| - | - |
| `piloop prompt <worker-id> "<text>"`, or `s` in `piloop watch` | Queues the message for the agent's next turn. It never cuts the turn in progress short. |
| `piloop attach <worker-id>` | Shows the agent in this terminal. |
| `enter` on an agent in `piloop watch` | Brings the agent to the front of the runtime's own view. |
| `piloop status`, `piloop watch` | Show where you can see the agent, for example its tab, when the runtime has such a place. |
| `piloop doctor` | Checks that the runtime can run agents on this machine. |

When a runtime cannot do one of these, the command says so, and tells you where to look instead. In every runtime,
the brief, log and transcript of an agent are in its log directory: `~/.piloop/runs/<run>/` for an agent that does a
run, and `~/.piloop/workers/<worker-id>/` for the sweeping roles.

## Built-in runtimes

| Runtime | What it is | Invocation | `piloop prompt` | `piloop attach` | `enter` in `piloop watch` |
| - | - | - | - | - | - |
| `herdr` | Each agent in a tab of its own in the piloop Herdr session, where you can read it and type into it. | `interactive` | Types the message into the agent's pane, where the agent keeps it until its turn ends. A harness that would end its turn to take the message has a queue key (`[harness.<name>] queue_key`), which piloop presses to queue it. While the agent waits on a dialog, the message is refused: answer the dialog in the agent's tab. | Focuses the agent's tab, then opens the piloop Herdr session in this terminal. In that session already, it only focuses the tab. You can then go to the other agents of the session. | Brings the agent's tab, and its workspace, to the front of the piloop Herdr session. |
| `subprocess` | Each agent as a background process without a terminal, one process per turn of the agent. | `headless` | Keeps the message in a queue, and continues the agent's session with it when the turn ends. piloop runs the harness again with `[harness.<name>] resume`, with the queued messages as the prompt. A harness without `resume` takes no message. Neither does an agent that the daemon re-attached to after `piloop restart`. | Cannot: the agent is a background process without a terminal. Its output is in `worker.stdout` instead. | Cannot: the agent is a background process without a terminal. Its output is in `worker.stdout` instead. |

The invocation is the argument list of a harness that the runtime starts. `headless` is a process without a terminal,
with the first prompt in its arguments. `interactive` is a session in a terminal, and the runtime types the first
prompt after the start. Some features of a harness depend on the invocation. For example, pi takes
[MCP servers](/guides/mcp-servers#what-each-harness-gets) only in its headless invocation.

## Choose a runtime

* `subprocess` is the default. It needs nothing more than the harnesses. `piloop watch` shows the output of each
  agent. Use it for a campaign that runs without you.
* `herdr` gives each agent a terminal pane. You can read each agent, answer its dialogs and type into it. It needs
  [Herdr](https://herdr.dev). See [In Herdr](/guides/watch#in-herdr) for the session, the workspaces and the tabs that
  piloop makes.

You can mix them: for example, `runtime = "subprocess"` for the project and `runtime = "herdr"` for research only.

## Add a runtime

A Python package adds a runtime with an entry point in the `piloop.runtimes` group. The class implements the
[`Runtime` protocol](/architecture/extension-points#runtimes). This runtime starts each agent as the `subprocess`
runtime does, at a lower CPU priority:

```python theme={null}
from dataclasses import replace
from typing import ClassVar

from piloop.harnesses import Invocation
from piloop.runtime import Handle, WorkerSpec
from piloop.runtime.subprocess import SubprocessRuntime


class NiceRuntime(SubprocessRuntime):
    """``nice``: background processes, like ``subprocess``, at a lower CPU priority."""

    kind: ClassVar[str] = 'nice'
    invocation: ClassVar[Invocation] = Invocation.HEADLESS

    def start(self, spec: WorkerSpec) -> Handle:
        return super().start(replace(spec, executable='nice', args=['-n', '10', spec.executable, *spec.args]))
```

```text theme={null}
[project.entry-points."piloop.runtimes"]
nice = "my_package:NiceRuntime"
```

Install the package in the environment of `piloop`. A project then sets `runtime = "nice"`, or a role sets it in its
own table. `piloop init` refuses a name that no runtime has. A name that a built-in runtime has already, a `kind` that
is not the name of the entry point, or a class without an `invocation` is an error that names the entry point.

`piloop attach`, `piloop prompt`, `piloop doctor` and `piloop watch` call the methods of the runtime, so they work with
the new runtime at once.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.