---
title: jr2Setup
description: "Set up a Machine that takes workflow events: the jr2 form of xstate's `setup()`."
---

# `jr2Setup`

Function, in `@jr2/orchestrator/src/setup.ts`. It is an export of `@jr2/orchestrator`, on the [Authoring surface](../authoring-surface.md) page, in Machine and events.

```ts
function jr2Setup<
  TContext extends MachineContext = MachineContext,
  const TDefs extends readonly EventDef[] = readonly [],
  TActors extends Record<string, UnknownActorLogic> = {},
  TActions extends Record<string, ParameterizedObject["params"] | undefined> = {},
  TGuards extends Record<string, ParameterizedObject["params"] | undefined> = {},
  TDelay extends string = never,
  TTag extends string = string,
  TInput = NonReducibleUnknown,
  TOutput extends NonReducibleUnknown = NonReducibleUnknown,
  TEmitted extends EventObject = EventObject,
  TMeta extends MetaObject = MetaObject,
>(
  def: {
    types?: {
      context?: TContext;
      input?: TInput;
      output?: TOutput;
      tags?: TTag;
      emitted?: TEmitted;
      meta?: TMeta;
    };
    events?: TDefs;
    actors?: TActors;
    actions?: {
      [K in keyof TActions]: ActionFunction<
        TContext,
        WorkflowEvent<TDefs>,
        WorkflowEvent<TDefs>,
        TActions[K],
        ToProvidedActor<MergedActors<TActors>>,
        ToParameterizedObject<TActions>,
        ToParameterizedObject<TGuards>,
        TDelay,
        TEmitted
      >;
    };
    guards?: {
      [K in keyof TGuards]: GuardPredicate<TContext, WorkflowEvent<TDefs>, TGuards[K], ToParameterizedObject<TGuards>>;
    };
    delays?: {
      [K in TDelay]: DelayConfig<
        TContext,
        WorkflowEvent<TDefs>,
        ToParameterizedObject<TActions>["params"],
        WorkflowEvent<TDefs>
      >;
    };
  } = {},
): SetupReturn<
  TContext,
  WorkflowEvent<TDefs>,
  MergedActors<TActors>,
  {},
  TActions,
  TGuards & SentByGuards,
  TDelay,
  TTag,
  TInput,
  TOutput,
  TEmitted,
  TMeta
>
```

Set up a Machine that takes workflow events: the jr2 form of xstate's `setup()`. Write a Machine
with it when an Agent or a Gate sends events into the Machine, or when the Machine declares a
door. Call `.createMachine(config)` on the result, as with `setup()`. The Machine you get is a
plain xstate Machine.

What it adds to `setup()`:
- `events` replaces `types.events`. The event type of the Machine comes from the
  `defineEvent` events you list, plus the events jr2 itself sends (`agent.fault`,
  `workspace.lost`). An event that a party sent has `event.sender`. A Machine that takes no
  workflow event leaves out `events`.
- The `gate` actor is ready to invoke by name: `invoke: { src: "gate" }`.
- Each Agent slot (`actors: { coder: agent(…) }`) gets its Menu from the states that invoke it.
- The `sentBy(id)` guard is ready to use.
- `createMachine` also takes `input`: a zod object for the input that starts a run. jr2 checks
  each start against it. Without it, a run can start with any input. The `setup()` of xstate
  does not read this `input`, so a Machine with a door uses `jr2Setup`, also when it takes no
  workflow event. `jr2 up` and the publish door refuse a registered Machine that has a zod
  `input` if `jr2Setup` did not make the Machine: ``workflow "<name>": machine "<id>" has a zod `input` on
  createMachine, but jr2Setup did not make this Machine, so no door reads that input …``

The output of a run is the root `output` of `createMachine`, beside `id` and `states`, as in
xstate v5. xstate does not read the `output` of a final state as the output of the Machine. It
gives that output to the root `output` as `event.output`. Write
`output: ({ event }) => event.output as Output` at the root, or make the output from `context`
there. `createMachine` refuses an `output` on a final state directly below the root when the root
has no `output` (see the list below).

`createMachine` refuses a Machine that it cannot run as written. Among the refusals:
- an `on` key that no listed event has: ``machine "<id>" handles event "<key>", which no def in
  jr2Setup({ events }) declares (declared: <names>) — a typo, or a missing defineEvent``
- a listed event that no state takes: ``machine "<id>" declares "<name>", which no state handles
  — a pick no state takes is on no Menu and no Gate: handle it, or drop it from
  jr2Setup({ events })``
- an entry of `events` that `defineEvent` did not make, or one name listed twice:
  ``machine "<id>": `events` entry is not a defineEvent() def: <entry>``,
  ``machine "<id>": duplicate event "<name>" in `events` ``
- an `agent()`, an Effect or a `workspace()` written on `invoke.src` instead of under `actors`:
  ``machine "<id>": state "<state>" invokes <part> inline — a Part is a key of actors, so it has
  a name that the ledger, the report and customize() share: actors: { <name>: <factory>(...) },
  then invoke: { src: "<name>" }``. `<part>` is `an Agent`, `an Effect` or `a Workspace`.
  `<factory>` is `agent`, the Effect's kind (such as `command`), or `workspace`.
- an `output` on a final state directly below the root, when the root has no `output`:
  ``machine "<id>": final state "<state>" declares an output, and the root has no output — xstate
  v5 gives a run only its root output, so this output reaches nothing and the run ends with none:
  write it at the root, as output: ({ context }) => ..., or keep it on the final state and pass it
  on with output: ({ event }) => event.output at the root``. With more than one such final state,
  it names each one and puts the words in the plural: ``final states "<state>", "<state>"
  declare an output``, ``these outputs reach nothing``, ``write one at the root`` and ``keep
  them on the final states and pass them on``. An `output` on a
  final state inside a compound state is legal: the `onDone` of that compound state gets it as
  `event.output`.
- a parallel state at or below a state that invokes an Agent: ``machine "<id>": state "<state>"
  is parallel, at or below Agent "<slot>"'s state "<state>" — a parallel region below an Agent's
  state is reserved for picks the Agent does not wait for, which jr2 does not build; background
  work goes above the Agent's state``
- a state below an Agent's state that invokes something and that nothing enters: ``machine
  "<id>": state "<state>" below Agent "<slot>"'s state "<state>" is entered by nothing, so what
  it invokes (<src>) never runs — target it from a pick or a route, or delete it``
- a state that invokes an Agent when that state and each state above it have no `agent.fault`
  route: ``machine "<id>": state "<state>" invokes an Agent, and no route for "agent.fault" is
  on it or on a state above — jr2 sends agent.fault when the Agent's Turn cannot go on, and with
  no route the run faults: route it on that state or on a state above it, as on: {
  "agent.fault": "<the state that handles it>" } (ADR-0074)``. A state that invokes an Open body
  that lends an Agent or allows a model gets the same line, with `invokes an Open body that can
  run an Agent`: the body sends it a Chart's unrouted `agent.fault`. When both kinds have no route,
  one line names both, joined by `, and`. One route on the root is a route for each state. A
  Chart's Machine does not get this refusal: in a Chart the route is optional ([ADR-0070](../decisions/0070-a-machine-lends-its-parts-to-a-chart.md)).

It also refuses a `sentBy` that names no party (see `sentBy`). It refuses a pick param that
nothing reads, where the pick enters a state that invokes an Effect (see `fromPick`). Shapes that
are legal but probably not what you mean, such as a mode that nothing enters, are warnings that
`jr2 up` prints.

**Parameters**

- `def`: What `setup()` takes, with `events` in place of `types.events`. A Machine that
  needs none of it calls `jr2Setup()`.

  - `types` (optional): `object`

    The types of the Machine, as in `setup()`: `context`, `input`, `output`, `tags`, `emitted`
    and `meta`. Not `events`: the event type comes from `events`.

    - `context` (optional): `TContext`
    - `input` (optional): `TInput`
    - `output` (optional): `TOutput`
    - `tags` (optional): `TTag`
    - `emitted` (optional): `TEmitted`
    - `meta` (optional): `TMeta`
  - `events` (optional): `TDefs`

    The workflow events this Machine takes: each one made by `defineEvent`. Leave it out when the
    Machine takes none. Each listed event must be the name of an `on` key. Each `on` key must be
    listed, if it is not a jr2 or xstate event (an event with a `.` in its name).
  - `actors` (optional): `TActors`

    The actors the states invoke by name, as in `setup()`: Agent slots (`agent(…)`), Effects
    (`command(…)`, `http(…)`, `push(…)`), child Machines, `workspace(…)` and plain xstate logic.
    The key is the slot's name. An actor named `gate` replaces jr2's Gate.
  - `actions` (optional): `{ [K in keyof TActions]: ActionFunction<TContext, WorkflowEvent<TDefs>, WorkflowEvent<TDefs>, TActions[K], ToProvidedActor<MergedActors<TActors>>, ToParameterizedObject<TActions>, ToParameterizedObject<TGuards>, TDelay, TEmitted>; }`

    Named actions, as in `setup()`. Their `event` has the type of this Machine's events.
  - `guards` (optional): `{ [K in keyof TGuards]: GuardPredicate<TContext, WorkflowEvent<TDefs>, TGuards[K], ToParameterizedObject<TGuards>>; }`

    Named guards, as in `setup()`. `sentBy` is there already; do not name a guard `jr2.sentBy`.
  - `delays` (optional): `{ [K in TDelay]: DelayConfig<TContext, WorkflowEvent<TDefs>, ToParameterizedObject<TActions>["params"], WorkflowEvent<TDefs>>; }`

    Named delays, as in `setup()`.

**Returns**: What `setup()` returns. Call `.createMachine(config)` on it.

**Example**

```ts
import { z } from "zod";
import { agent, defineEvent, fail, jr2Setup } from "@jr2/orchestrator";

const proposed = defineEvent({
  name: "proposed",
  description: "Call this with your plan.",
  input: z.object({ plan: z.string() }),
});
const approve = defineEvent({ name: "approve", input: z.object({}) });
const reject = defineEvent({ name: "reject", input: z.object({ reason: z.string() }) });

export const machine = jr2Setup({
  types: {} as { input: { goal: string }; context: { goal: string } },
  events: [proposed, approve, reject],
  actors: {
    planner: agent({
      model: "anthropic/claude-haiku-5-5",
      instructions: "You plan small releases.",
      workspace: "none",
    }),
    fail: fail(),
  },
}).createMachine({
  id: "plan",
  input: z.object({ goal: z.string() }),
  context: ({ input }) => ({ goal: input.goal }),
  on: { "agent.fault": ".failed" },
  initial: "planning",
  states: {
    planning: {
      invoke: { src: "planner", input: ({ context }) => ({ prompt: `Plan this: ${context.goal}` }) },
      on: { proposed: "review" },
    },
    review: {
      invoke: { src: "gate" },
      on: { approve: "approved", reject: "planning" },
    },
    approved: { type: "final" },
    failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
  },
});
```

**See also**

- [Write a Workflow](../../guides/write-a-workflow.md)
- [Machines and Workflows](../../concepts/machines-and-workflows.md)
- [ADR-0015: Workflows are authored through `jr2Setup`; vocabulary and agent menus derive from the machine](../decisions/0015-authoring-surface-absorbs-the-mechanism.md)
