---
title: "@jr2/machines"
description: "The Machines that @jr2/machines ships: their input, their output, and their Open parts."
---

# @jr2/machines

`@jr2/machines` ships Machines, not Workflows. A Machine becomes a Workflow when a file in the Instance's `workflows/` folder exports it, with each [Open](glossary.md#open) part bound by [`customize`](authoring-surface/customize.md). Install it beside `@jr2/orchestrator`, at the same version: it is a peer of the kit, never a copy of it.

## Contents

- [`TaskInput`](#type-taskinput), [`TaskOutput`](#type-taskoutput), [`body`](#body), [`task`](#task)

<a id="type-taskinput"></a>

### `TaskInput`

Type, in `@jr2/machines/src/task.ts`.

```ts
type TaskInput = {
  prompt: string;
  branch?: string | undefined;
  model?: string | undefined;
  thinkingLevel?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | undefined;
};
```

The run input of `task`: what a caller sends to start a run, for example with
`jr2 run <workflow> --input '{"prompt":"…"}'`.

`prompt` is required. `branch`, `model` and `thinkingLevel` are optional. Each field has its own
doc.

**Fields**

- `prompt`: `string`

  What to do, in prose. Required. The coder's first Turn is this prompt, with the Worktree it
  works in.
- `branch` (optional): `string`

  The branch to cut and commit on. Default: `jr2/task-<instance id>`, where `<instance id>` is
  the seed Instance ID of the run (`instanceId` in `jr2 status`). It is not the run id. Two runs
  never use the same branch. Required when `task` runs inside another Machine, such as a
  `pool()`: there is no Instance ID to name the branch after.
- `model` (optional): `string`

  The coder's model for this run only, as `"<provider>/<modelId>"`. Default: the model of the
  coder Agent.
- `thinkingLevel` (optional): `"off" | "minimal" | "low" | "medium" | "high" | "xhigh"`

  The coder's thinking level for this run only: `off`, `minimal`, `low`, `medium`, `high` or
  `xhigh`. Default: the thinking level of the coder Agent.

**Example**

```ts
import type { TaskInput } from "@jr2/machines";

export const input: TaskInput = {
  prompt: "Make the failing test in test/greet.test.ts pass.",
  branch: "fix/greet",
  thinkingLevel: "high",
};
```

<a id="type-taskoutput"></a>

### `TaskOutput`

Type, in `@jr2/machines/src/task.ts`.

```ts
type TaskOutput = { branch: string };
```

The output of a run of `task` that ends `done`: `{ branch }`, the branch of the work that the
human approved. A run whose Workspace is lost has no output of this type: it fails, and the run
ends `failed` with the reason `the Workspace was lost` ([ADR-0073](decisions/0073-a-machine-fails-a-run-with-fail-and-an-uncaught-failure-ends-it-failed.md)).

**Fields**

- `branch`: `string`

**Example**

```ts
import type { TaskOutput } from "@jr2/machines";

export function describe(output: TaskOutput): string {
  return `approved on ${output.branch}`;
}
```

<a id="body"></a>

### `body`

Constant, in `@jr2/machines/src/task.ts`.

```ts
const body: StateMachine<…>
```

Type: `StateMachine`, from `xstate`.

The Machine inside `task`: the coder's loop and the `review` Gate, with no `workspace()` around
it. Use `task` to run it. Use `body` only to put the same loop in a `workspace()` of your own.
For example, your `workspace()` can have Repo Slots or a run input that differ from those of
`task`. Its input is the run input plus the `workspace` handles, and it works in the first Repo
Slot of those handles.

**Example**

```ts
import { customize, workspace } from "@jr2/orchestrator";
import { body, type TaskInput } from "@jr2/machines";

const fixed = workspace(body, {
  repos: { app: { url: "https://github.com/you/app.git" } },
  spec: ({ input }: { input: TaskInput }) => ({ branch: input.branch ?? "jr2/fix" }),
});

export const machine = customize(fixed, {
  agents: { coder: { model: "anthropic/claude-haiku-5-5" } },
});
```

**See also**

- [Workspaces](../concepts/workspaces.md)

<a id="task"></a>

### `task`

Constant, in `@jr2/machines/src/task.ts`.

```ts
const task: WorkspaceMachine<…>
```

Type: [`WorkspaceMachine`](authoring-surface/type-workspacemachine.md).

A Machine in which a coder Agent works on one prompt in a Workspace until a human approves the
work. The coder works in a Sandbox and commits on a branch. Then the run waits at the `review`
Gate. The human answers `request_changes` with `notes`, and the coder continues the same
conversation, or `approve`, and the run ends. A run has no limit on the number of rounds.

`task` does not push. While the run waits at the Gate, the Sandbox stays. You can exec into it
and read the branch. To keep the work, push it yourself. `approve` removes the Sandbox, and
every commit that was not pushed goes with it. If the Sandbox is lost, `task` fails: the run
ends `failed` with the reason `the Workspace was lost`. A parent Machine catches that Failure
with an `onError` that `isFailure` guards.

`task` leaves two parts Open, so you bind them with `customize()` where you register it:

- `repos`: the whole Repo Slot map. Name every slot, and at least one. The first slot is the
  Repo that the coder works in. The coder can read the others.
- `agents.coder.model`: the coder's model, as `"<provider>/<modelId>"`. You can also change the
  coder's other fields, such as `instructions` or `thinkingLevel`.

`jr2 up` refuses a Workflow that leaves either part Open, and prints the `customize()` line
that binds it. You can also give `task` an `image`, a `user` or a Size (`resources`). Without an
image, it uses the Instance's `images/default`, or jr2's Harness image when there is none.
Without a Size, it uses the Instance's `sandbox.resources`, or the kit's Size when it has none.
Inside another Machine, `task` needs `branch` in its input, or it refuses:
``task: no `branch` on the door and no Instance ID to derive one from``.

**Example**

```ts
import { customize } from "@jr2/orchestrator";
import { task } from "@jr2/machines";

export const machine = customize(task, {
  repos: {
    app: { url: "https://github.com/you/app.git" },
    lib: { url: "https://github.com/you/lib.git" },
  },
  agents: { coder: { model: "anthropic/claude-haiku-5-5" } },
});
```

**See also**

- [An Agent in a Sandbox](../get-started/an-agent-in-a-sandbox.md)
- [Bind a shipped Machine's Open parts](../guides/bind-open-parts.md)
- [ADR-0054: The kit ships Machines, and a shipped Machine leaves its parts Open](decisions/0054-the-kit-ships-machines-and-a-shipped-machine-leaves-its-parts-open.md)
