---
title: fromPick
description: "Make the `input` of the invoke of a state that a pick enters: the params of the pick go to the input of the invoked actor by name."
---

# `fromPick`

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

## Overload 1

```ts
function fromPick<
  N extends string,
  I extends z.ZodObject,
  C,
  E extends EventObject,
>(
  def: EventDef<N, I>,
): (args: { context: C; event: E; self: AnyActorRef }) => z.infer<I>
```

Make the `input` of the invoke of a state that a pick enters: the params of the pick go to the
input of the invoked actor by name. With `fill`, the Machine adds the keys that the Agent must
not choose. So one expression shows the security boundary: the pick is what the Agent gives, and
`fill` is what the Machine adds.

TypeScript checks the params and the keys of `fill` together against the input type of the
invoked actor. It finds a missing key or a key of the wrong type, but it accepts an extra key.

So `createMachine` refuses:
- a param that is no key of the input of the Effect, and that nothing else reads: ``machine
  "<id>": pick "<pick>" enters state "<state>" (Effect "<slot>") with fromPick: "<param>" is no
  key of the Effect's input (<keys>), and nothing else reads it — the Agent supplies it and it
  reaches nothing; name an argv slot for it, read it in `fill`, store it with assign, or drop it
  from the def``
- a key that `fill` sets and that is also a param: ``machine "<id>": pick "<pick>" enters state
  "<state>" (Effect "<slot>"): `fill` sets "<param>", which is also a param of the pick — the
  Machine's value wins and the Agent's is dropped; rename one, or drop it from the def``
- a `fromPick` in a state that another event enters: `machine "<id>": state "<state>" (Effect
  "<slot>") reads the params of pick "<pick>" but is entered on "<event>" — that event does not
  carry them`

The input of an Open argv is one string for each key, so TypeScript checks no key for it. A key
that the bound argv names and the input does not give stops the Effect when it starts.

The state that the pick enters has one of two places:
- A child state of the Agent's state, as in the first example. The Turn stays open, and the
  pick is outstanding until the Effect ends. Use this shape when the Agent must see the result.
  The Agent reads the Effect's output as the answer only when the route after the Effect stays
  in the Agent's state, as `onDone: "ready"` does. Then the Agent can pick again. A route that
  leaves the Agent's state ends the Turn when the Effect ends, and no Agent reads the output.
- A sibling state of the Agent's state, as in the second example. The pick leaves the Agent's
  state, so the Turn ends before the Effect runs, and the Agent does not see the output. Use
  this shape for a pick that ends the work of the Agent.

**Parameters**

- `def` ([`EventDef`](type-eventdef.md)): The pick, as `defineEvent` made it.

**Returns**: An `input` mapper for the `invoke` of the state.

**Example**

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

const postNote = defineEvent({
  name: "post_note",
  description: "Post a note to the team channel.",
  input: z.object({ text: z.string() }),
});

const finish = defineEvent({
  name: "finish",
  description: "The note is posted. This ends your work.",
  input: z.object({}),
});

export const machine = jr2Setup({
  types: {} as { context: {}; input: {} },
  events: [postNote, finish],
  actors: {
    writer: agent({
      model: "anthropic/claude-haiku-5-5",
      instructions: "You write one short note for the team.",
      workspace: "none",
    }),
    note: http({
      method: "POST",
      url: "https://hooks.example.com/notes",
      input: z.object({ text: z.string() }),
      request: ({ text }) => ({ json: { text } }),
    }),
    fail: fail(),
  },
}).createMachine({
  id: "note",
  on: { "agent.fault": ".failed" },
  initial: "writing",
  states: {
    writing: {
      invoke: {
        src: "writer",
        input: {
          prompt: "Write a note that says the release is out, post it, and check the reply.",
        },
      },
      initial: "ready",
      states: {
        ready: { on: { post_note: "posting", finish: "#done" } },
        posting: {
          // The `text` of the pick goes to the input of `note` as it is. The route goes back
          // to `ready`, in `writing`: the writer reads the output and picks again.
          invoke: { src: "note", input: fromPick(postNote), onDone: "ready" },
        },
      },
    },
    done: { id: "done", type: "final" },
    failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
  },
});
```

**Example**

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

const postNote = defineEvent({
  name: "post_note",
  description: "Post the note to the team channel. This ends your work.",
  input: z.object({ text: z.string() }),
});

export const machine = jr2Setup({
  types: {} as { context: {}; input: {} },
  events: [postNote],
  actors: {
    writer: agent({
      model: "anthropic/claude-haiku-5-5",
      instructions: "You write one short note for the team.",
      workspace: "none",
    }),
    note: http({
      method: "POST",
      url: "https://hooks.example.com/notes",
      input: z.object({ text: z.string() }),
      request: ({ text }) => ({ json: { text } }),
    }),
    fail: fail(),
  },
}).createMachine({
  id: "note",
  on: { "agent.fault": ".failed" },
  initial: "writing",
  states: {
    // The pick leaves this state, so it ends the Turn of the writer.
    writing: {
      invoke: {
        src: "writer",
        input: { prompt: "Write a note that says the release is out, and post it." },
      },
      on: { post_note: "posting" },
    },
    // A sibling of `writing`: the Effect runs after the Turn, and no Agent reads its output.
    posting: {
      invoke: { src: "note", input: fromPick(postNote), onDone: "done" },
    },
    done: { type: "final" },
    failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
  },
});
```

**See also**

- [Effects](../../concepts/effects.md)
- [Run a command or call a URL with an Effect](../../guides/run-a-command-or-call-a-url.md)
- [ADR-0067: An Effect is an actor slot, and runs at most once unless marked idempotent](../decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md)

## Overload 2

```ts
function fromPick<
  N extends string,
  I extends z.ZodObject,
  C,
  E extends EventObject,
  R extends object,
>(
  def: EventDef<N, I>,
  fill: (args: { context: C; params: z.infer<I> }) => R,
): (args: { context: C; event: E; self: AnyActorRef }) => z.infer<I> & R
```

Make the `input` from the params of the pick and the keys that `fill` adds. `fromPick`
tells the rest, and what `createMachine` refuses.

**Parameters**

- `def` ([`EventDef`](type-eventdef.md)): The pick, as `defineEvent` made it.
- `fill`: Gets `{ context, params }` and gives back only the keys that the Machine adds. Do
  not set a key that is also a param.

**Returns**: An `input` mapper for the `invoke` of the state.

**Example**

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

const postComment = defineEvent({
  name: "post_comment",
  description: "Post a comment on the ticket.",
  input: z.object({ body: z.string() }),
});

export const machine = jr2Setup({
  types: {} as { context: { ticket: string }; input: { ticket: string } },
  events: [postComment],
  actors: {
    triager: agent({
      model: "anthropic/claude-haiku-5-5",
      instructions: "You triage tickets.",
      workspace: "none",
    }),
    comment: http({
      method: "POST",
      url: "https://jira.example.com/rest/api/2/issue",
      auth: bearer(secret("jira")),
      input: z.object({ ticket: z.string(), body: z.string() }),
      request: ({ ticket, body }) => ({ path: [ticket, "comment"], json: { body } }),
    }),
    fail: fail(),
  },
}).createMachine({
  id: "triage",
  context: ({ input }) => input,
  on: { "agent.fault": ".failed" },
  initial: "triaging",
  states: {
    triaging: {
      invoke: {
        src: "triager",
        input: ({ context }) => ({
          prompt: `Read ticket ${context.ticket}, then post one comment on it.`,
        }),
      },
      initial: "ready",
      states: {
        ready: { on: { post_comment: "posting" } },
        posting: {
          invoke: {
            src: "comment",
            // The Agent gives `body`. The Machine gives `ticket`, so the Agent cannot post on
            // another ticket.
            input: fromPick(postComment, ({ context }) => ({ ticket: context.ticket })),
            onDone: "#done",
          },
        },
      },
    },
    done: { id: "done", type: "final" },
    failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
  },
});
```

**See also**

- [Effects](../../concepts/effects.md)
- [Run a command or call a URL with an Effect](../../guides/run-a-command-or-call-a-url.md)
- [ADR-0067: An Effect is an actor slot, and runs at most once unless marked idempotent](../decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md)
