---
title: answer
description: Set what the Agent reads when jr2 answers its pick.
---

# `answer`

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

```ts
function answer<
  TContext extends MachineContext,
  TExpressionEvent extends EventObject,
  TParams extends ParameterizedObject["params"] | undefined,
  TEvent extends EventObject,
  TActor extends ProvidedActor,
>(
  fn: (args: { context: TContext; event: TExpressionEvent; chain: readonly ChainLink<TActor, TEvent>[] }) => unknown,
): ActionFunction<TContext, TExpressionEvent, TEvent, TParams, TActor, never, never, never, never> & AnswerAction
```

Set what the Agent reads when jr2 answers its pick. It is an xstate action: write it in
`entry`, `exit` or the `actions` of a transition. Without it, the Agent reads the last reply
since the pick.

`fn` gets `{ context, event, chain }`. `chain` is every reply since the pick, oldest first. Each
link is `{ kind: "output", src, output }`, the output of an invoked actor, or `{ kind: "event",
event }`, an event that a Gate or a nested Agent sent. TypeScript types each link, but not the
order or the number of links. What `fn` gives back goes to the Agent as JSON. A value with no
JSON form clears the answer: the Agent then reads the last reply. When more than one `answer`
runs, the last one wins.

Where you write it decides which pick it answers:

- At or below the state of an Agent, it answers only a pick of that Agent.
- On a transition below no Agent's state, it answers a pick of the Agent whose event took the
  transition.
- On an `entry` or an `exit` below no Agent's state, it sets nothing, and `jr2 up` prints a
  warning: `an answer on the <entry|exit> of "<state>" is below no Agent's state, so it shapes
  no pick — an <entry|exit> there is no transition an Agent's event takes; move it under
  "<agent state>", or onto a transition (ADR-0067)`.

The error of an invoked actor is no link, and the Agent does not see it. To tell the Agent,
write `answer` on the `onError` route. An Effect never takes that route for an outcome: every
outcome is output.

**Parameters**

- `fn`: Gets `{ context, event, chain }` and gives back what the Agent reads, as a value
  with a JSON form.

**Returns**: An xstate action.

**Example**

```ts
import { z } from "zod";
import {
  agent,
  answer,
  command,
  defineEvent,
  fail,
  jr2Setup,
  workspace,
  type Workspaced,
} from "@jr2/orchestrator";

const runTests = defineEvent({
  name: "run_tests",
  description: "Run the test suite. Its result is this call's answer.",
  input: z.object({}),
});

const finished = defineEvent({
  name: "finished",
  description: "Say that the tests pass.",
  input: z.object({}),
});

type Input = Workspaced<{}, "app">;

const body = jr2Setup({
  types: {} as { context: Input; input: Input },
  events: [runTests, finished],
  actors: {
    coder: agent({
      model: "anthropic/claude-haiku-5-5",
      instructions: "You are a careful engineer.",
    }),
    tests: command(["npm", "test"]),
    fail: fail(),
  },
}).createMachine({
  id: "fix-tests",
  context: ({ input }) => input,
  on: { "workspace.lost": ".lost", "agent.fault": ".failed" },
  initial: "coding",
  states: {
    coding: {
      invoke: { src: "coder", input: { prompt: "Make the tests pass." } },
      on: { finished: "done" },
      initial: "ready",
      states: {
        ready: { on: { run_tests: "testing" } },
        testing: {
          invoke: {
            src: "tests",
            onDone: {
              target: "ready",
              // The Agent reads this string, not the whole output of the command.
              actions: answer(({ event }) =>
                event.output.outcome === "exited" && event.output.code === 0
                  ? "The tests pass."
                  : `The tests fail:\n${"tail" in event.output ? event.output.tail : event.output.reason}`,
              ),
            },
          },
        },
      },
    },
    done: { type: "final" },
    failed: { invoke: { src: "fail", input: { reason: "the Agent's Turn could not go on" } } },
    lost: { invoke: { src: "fail", input: { reason: "the Sandbox is gone" } } },
  },
});

export const machine = workspace(body, {
  repos: { app: { url: "https://github.com/you/app.git" } },
  spec: () => ({ branch: "fix-tests" }),
});
```

**See also**

- [The Menu](../../concepts/the-menu.md)
- [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)
- [ADR-0024: An Agent's turn ends with the state that asked for it](../decisions/0024-an-agents-turn-ends-with-the-state-that-asked-for-it.md)
