Skip to content

Customize ​

Type, in @jr2/orchestrator/src/customize.ts. It is an export of @jr2/orchestrator, on the Authoring surface page, in Composition.

ts
type Customize<M extends AnyStateMachine> = {
  agents?: [AgentSlots<Reached<M>>] extends [never]
    ? never
    : { [K in AgentSlots<Reached<M>>]?: Partial<AgentDefinition> };
  effects?: [OpenEffectSlots<Reached<M>>] extends [never]
    ? never
    : { [K in OpenEffectSlots<Reached<M>>]?: BindingOf<LogicAt<Reached<M>, K>> };
  actors?: [ChildSlots<Reached<M>>] extends [never]
    ? never
    : { [K in ChildSlots<Reached<M>>]?: Customize<ChildAt<Reached<M>, K>> };
  image?: string;
  user?: string | UserContainer;
  resources?: Size;
  repos?: [RepoSlotsOf<M>] extends [never] ? never : { [K in RepoSlotsOf<M>]?: RepoSlot<InputFrom<WorkspaceOf<M>>> };
};

The parts of one Machine that customize() can retune: its Agents, its Open Effects, the Machines it composes, and the Sandbox of its workspace(). The type comes from the Machine itself, so it offers only the parts that the Machine declares. A key for a part the Machine does not declare does not compile. The agents, effects, actors and repos keys take no value when the Machine has no such part. image, user and resources compile on every Machine, and customize() refuses them when the Machine composes no workspace().

Every key is optional. Write only what you change.

Fields

  • agents (optional): [ AgentSlots<Reached<M>> ] extends [ never ] ? never : { [K in AgentSlots<Reached<M>>]?: Partial<AgentDefinition>; }

    Retune the Agents that this Machine carries, by slot name: { coder: { model } }. Each override goes over the definition that the author wrote, field by field, so { model } alone keeps the author's instructions. When the author left the model Open, this is where you bind it. Allowed fields: the fields of an agent() definition. At run time, customize() refuses a slot that holds no Agent: customize(): machine "<id>" carries no Agent slot "<slot>" — its Agents are: <slots>.

  • effects (optional): [ OpenEffectSlots<Reached<M>> ] extends [ never ] ? never : { [K in OpenEffectSlots<Reached<M>>]?: BindingOf<LogicAt<Reached<M>, K>>; }

    Bind the Open parts of this Machine's Effects, by slot name: { url } for an http Effect whose url is Open, { argv } for a command whose argv is Open, { secret } for an Effect that names secret(open), { target: { url, token: secret("<name>") } } for a dispatch whose target is Open. For an Effect with two Open parts, one object holds both keys, and a binding that leaves one out does not compile. Only Effects with an Open part are offered. See EffectBinding for the values and the refusals.

  • actors (optional): [ ChildSlots<Reached<M>> ] extends [ never ] ? never : { [K in ChildSlots<Reached<M>>]?: Customize<ChildAt<Reached<M>, K>>; }

    Retune a Machine that this Machine composes, by slot name. The value has the same shape as these parts, one level down. At run time, customize() refuses a slot that holds no Machine: customize(): machine "<id>" composes no Machine under "<slot>".

  • image (optional): string

    The Sandbox Image: a file: URL to a docker context that your module ships (import.meta.resolve("./image")), which jr2 up builds, or a registry ref. It replaces the image of the workspace(). When the Machine composes no workspace(), customize() refuses it at run time.

  • user (optional): string | UserContainer

    The User Container: its image, as a file: URL or a registry ref, or a UserContainer that also gives its part of the Size. It replaces the whole User Container, its part of the Size included. When the Machine composes no workspace(), customize() refuses it at run time.

  • resources (optional): Size

    The Size, layered field by field over the Size of the workspace(): { limits: { memory } } keeps the author's cpu. Only limits.memory and limits.cpu. See Size. Only for a Machine that composes a workspace().

  • repos (optional): [ RepoSlotsOf<M> ] extends [ never ] ? never : { [K in RepoSlotsOf<M>]?: RepoSlot<InputFrom<WorkspaceOf<M>>>; }

    Bind the Repo Slots of the workspace(), by slot name. Each value is one of these:

    • a url
    • a Binding { url, ref? }
    • a function ({ input }) => url | { url, ref? } over the run input
    • open, to leave the slot for the next composer to bind

    You can bind an Open slot, or give a bound or per-run slot a different Binding. A slot that the Machine does not declare does not compile. Some Machines, such as task, leave their whole slot map Open. Then you name every slot, and at least one. The keys are your names for the Repos, and the Machine says what their order means.

Example

ts
import { customize, type Customize } from "@jr2/orchestrator";
import { task } from "@jr2/machines";

const parts: Customize<typeof task> = {
  repos: { app: { url: "https://github.com/you/app.git", ref: "main" } },
  agents: { coder: { model: "anthropic/claude-haiku-5-5", thinkingLevel: "high" } },
};

export const machine = customize(task, parts);

See also

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