Skip to content

Why jr2 ​

jr2 runs an agentic workflow as a Machine: a graph of states that you write. In a state, an Agent or an Effect does the work, or a Gate waits for an answer. The Machine decides which state comes next. The diagram shows the parts of jr2, from you to the world outside your cluster.

Youstart runs, and answer them
jr2 CLI
Console
your own app
your clusterjr2 up sets it up
Orchestratorruns your Machine, and keeps each run
codereviewapprovaldone
approvedchangeschanges
Sandboxone pod for each Workspace
Harnessruns each Turn, with a shell
coder Agentedits and commits
reviewer Agentits Menu: approved changes
Worktreeyour code
Custodiankeys that the Agents can use but not read, such as the model key
Effectorkeys that only your Effects use, such as a git credential
Outsidewhat the work uses
model provider
your APIs
git host
The parts of jr2, from you to the world outside your cluster

Each section below gives one problem of agentic workflows, and the part of the diagram that solves it. The sections go from the top of the diagram to the bottom.

Headless ​

A workflow must connect to the systems that you use, such as a code review, a chat or a CI job. One user interface cannot be the right one for each of them.

The You band of the diagram holds the clients of jr2. The Orchestrator is an HTTP API. The jr2 CLI and the Console are its clients, and your own app, such as a webhook, can be one too. For example, a client can start a run with POST /workflows/:name/runs. In the diagram, a Gate in the state approval asks you, and you answer from any of these clients, with POST /runs/:runId/gates/:gate/events.

Read Architecture and Gates.

Determinism ​

A workflow must do the steps that it sets, in the order that it sets. But a model does not do the same thing two times. When the steps of a workflow are text in a prompt, the model can skip a step or change the order. "You must" and "never" are requests to the model, not rules that it must obey. You can review the code that an agentic workflow makes, but you cannot easily review how it made that code.

In jr2, a workflow is a Machine: an xstate state machine in TypeScript. The Orchestrator band of the diagram holds an example Machine, with the states code, review and approval, and the final state done. Its states and its transitions are a graph that you can read before a run starts. The Console draws that graph, and while a run runs, it shows the state that the run is in. A Machine can have loops: in the diagram, changes sends the run back to code.

An Agent does not move the Machine itself. A Turn is one piece of work for one Agent. The state that the run is in gives the Agent a prompt and a Menu: the events that go out of that state. In the diagram, the run is in review, so the Menu of the reviewer is approved and changes. The Agent ends its Turn with one pick from its Menu, and the Machine refuses a pick that is not one of its Allowed picks now. Thus the model makes the decision in each state, and the Machine sets the transition that each decision causes. You review how the work happens, not only the code that comes out of it. The model is not deterministic, but the Machine is: in one state, one pick always causes the same transition.

The Agent can also decide the order of the work. A pick whose target is inside the Agent's state does not end the Turn. The Machine runs the work of the pick, such as an Effect or a Gate, and answers the pick with the result. A mode or a guard sets which picks the Agent can make next. Thus the Agent decides when each step runs, and the Machine decides what each step does. An Agent can also write a Chart: a Machine as data, which jr2 checks before it runs.

Read Machines and Workflows, The Menu and Charts.

Composition ​

An Agent does one thing well. When you give it many things at the same time, it loses its focus. The infrastructure of a workflow, such as a Sandbox or a set of work items, also must not get into the task of the Agent.

The diagram shows one Machine, but the infrastructure around it is xstate too. A Workspace wraps a body Machine that does not know about the Sandbox. A Pool runs one worker Machine for each item that a Source gives. A Gate is an actor that a state invokes to wait for an outside input, such as the Gate in approval. Each state sets the Frame of one Turn, so an Agent does one thing at a time.

A Machine can invoke a different Machine. A Machine can also leave a part Open, such as the URL of a Repo or the model of an Agent. You bind that part with customize(). Thus a Machine can come from an npm package, such as the task Machine in @jr2/machines.

Read Composition and Pools and Sources.

Isolation ​

A filesystem sandbox is not isolation. Agents on one computer share its ports, its processes and its credentials. Two Agents can need the same port, or the same file in your home folder. An Agent can also leave a process or a server that runs after the work stops.

The Sandbox band of the diagram is one pod. An Agent that works on a Repo works in a Sandbox: a pod with its own filesystem, network and processes. There is one Sandbox for each Workspace, and the Agents of one Workspace share it. The Harness runs each Turn. It talks to the model, and it runs the tools of the Agent. The Agent can run each command in the Harness container. An Agent that needs only its Menu has no Sandbox. Its Turns run on the Instance Harness.

The Harness container holds no credential, so a command that an Agent runs cannot read one. jr2 keeps each credential in one of the two containers beside it:

  • The Custodian holds each Held secret: a key that the Agents can use, but cannot read. The Harness holds only a Stand-in for it. The Custodian puts the real key in place of the Stand-in, in each request to the host of that key. A Stand-in has no value outside its pod. The model key is a Held secret. A token for an API that the Agents call can be one too.
  • The Effector holds each Effect secret: a key that only an Effect of your Machine uses, such as a push or a call to an API. The Agents cannot use it.

Put a key in the Custodian when the Agents can use it as they decide. Put it in the Effector when only your Machine decides when to use it. One key can be in both containers.

When the Workspace ends, its Sandbox ends too. When a Lease lapses, the operator removes what the Agents left behind.

Read Workspaces and Secrets and trust.

Model agnostic ​

A workflow must not depend on one model or one provider. You can change the model of one Agent, and keep the Machine.

In the diagram, the line from the Custodian goes to a model provider outside your cluster. jr2 has one Harness, and it is jr2's own. An Agent is a model and its instructions. The model is a name such as <provider>/<model>. The provider is one from the model catalog of the Harness, or an endpoint that you run and set in harness.provider. The same Harness runs the Turns of each Agent, with each provider. A state can set the model of one Turn.

Read Agents and Turns.

Distribution ​

One computer has a limit. One build can use all of its CPU, and the other Agents then wait. A run on a laptop also stops when the laptop stops.

In the diagram, the bracket is your cluster, and the compute is in it. For each Agent, the Orchestrator holds only an Actor: a local handle. The Harness that runs the Turns of the Agent runs where the cluster puts it. A Sandbox can go on each node that can run an ordinary pod. The Orchestrator keeps a snapshot of each run on its volume, so a run continues after a restart of the Orchestrator. On a shared cluster, a run continues when your computer is off.

Read Architecture.

One converging command ​

A workflow that runs on a laptop must also run on a shared cluster, without a second set of steps.

The bracket of the diagram has the label jr2 up. jr2 up converges an Instance folder into the cluster of the current kubectl context. It builds the images that the folder needs, and it puts the Orchestrator and the operator in the cluster. It does not start a Sandbox: during a run, the operator makes one Sandbox for each Workspace. The same folder converges into kind on a laptop or into a shared cluster. You select the cluster with the kubectl context. A shared cluster also needs a container registry, which .env gives to registry. You can run jr2 up again at any time.

Read Converge.

A typed Instance ​

An error in a workflow or in its config can show only when a run gets to it. A run can take many minutes to get there.

In jr2, a Machine names its Agents, its Repo Slots and the Machines that it invokes, and TypeScript checks each name. jr2.config.ts uses defineConfig(), so TypeScript checks the config too. jr2 up runs the typecheck first. When the typecheck fails, jr2 up builds nothing and changes nothing on the cluster. Next, it refuses an Open part that no customize() binds. Thus you find these errors before a run starts.

Read Converge and Machines and Workflows.

One run ​

These steps follow one run of the example Machine in the diagram. You write a Machine like it in TypeScript, or your Assistant writes it for you.

  1. You start a run. You run jr2 run feature, with the ticket as the input of the run. The Machine starts in its first state, code.
  2. The coder works in a Sandbox. The state code is in a Workspace, so jr2 makes a Sandbox for it. The Sandbox holds a Worktree of your Repo, on the branch of the run. The coder edits the files, runs the tests and commits. Then it picks finish, and the Machine moves to review.
  3. The reviewer gets a Turn. The state review gives the reviewer a prompt and its Menu: approved and changes. The reviewer reads the change in the same Sandbox.
  4. The reviewer picks. If it picks approved, the Machine moves to approval. If it picks changes, the Machine goes back to code, and the coder continues its work.
  5. The run waits for you. In approval, a Gate holds the run until you answer. You approve the work, or you ask for changes. You answer in the Console, with jr2 send, or from your own app.
  6. The run ends. The Machine gets to done. The Workspace ends, and its Sandbox pod stops with it.

The task Machine in @jr2/machines is a smaller Machine of this kind: a coder in a Sandbox, and a Gate that you answer. The Get started tutorial runs it.

The docs of Kit version 0.5.1. Each page is also at its .md URL.