---
title: The attempt context
description: >-
  The context every eval body gets, the Turn that c.send returns, and the shapes
  they use.
---
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reeval.ai/llms.txt
> Use this file to discover all available pages before exploring further.

Every eval body gets `c`, an `AttemptContext`: it sends turns to the agent, runs commands in the sandbox, reads files and asks the judge.

```ts title="evals/greet.eval.ts"
import { evaluate, expect } from '{{ pkg }}'

evaluate('creates a tested greet module', async (c) => {
  const turn = await c.send('Create greet.js exporting greet(name).')
  await expect(turn).toHaveFile('greet.js', /export/)
  await expect(c.exec('node --check greet.js')).toSucceed()
  expect(await c.judge('Is greet.js small and readable?')).toPass()
})
```

Each attempt gets a fresh `c`, a fresh sandbox and a fresh agent session. They start on the first call that needs them (`send`, `exec`, `readFile`, `writeFile`, `serve` or `capture`), so the body's own code runs first. Calls made at once share that one start, and a failed start fails the attempt as an infra error.

## Methods

<Property name="send" type="(prompt: string) => Promise<Turn>">
  Sends the next turn of the agent session and waits for it to finish. Later calls continue the same session, so the agent keeps its context. It resolves even when the turn ends in `error`; check `turn.status` when that matters.
</Property>

<Property name="exec" type="(command: string, options?: { background?: boolean, retries?: number, interval?: number }) => Promise<ExecResult | BackgroundHandle>">
  Runs a shell command in the sandbox's workspace. Without `background` it resolves with `{ exitCode, stdout, stderr }` and doesn't throw on a non-zero exit: use `toSucceed()`. With `retries`, a non-zero exit runs the command again, up to that many extra times, `interval` milliseconds apart (default 1000), and it resolves with the last try: see [Retrying a command](#retrying-a-command). `c.exec(cmd, { background: true })` starts the command without waiting for exit and returns `{ kill() }`; teardown kills it if the eval didn't. The whole command runs in its own shell, so `&&`, `;` and its redirects behave as in the foreground. It sees secrets sent `to: 'checks'`, which the agent never does. Before any turn it checks what the environment set up.
</Property>

<Property name="serve" type="(command: string, port?: number) => Promise<ServeHandle>">
  Starts `command` in the background and returns `{ kill, port }`. A given `port` is used; omit it and reeval allocates one. Sets `PORT` so a bash string can use `$PORT`. `{port}` is not expanded unless the command is argv.
</Property>

<Property name="capture" type="(target: number | ServeHandle, options?: CaptureCall) => Promise<Capture>">
  Screenshots a path on a tunneled sandbox port and writes `artifacts/<name>.png`. The first argument is the container port or a `ServeHandle` from `c.serve`. Options are `{ name?, path?, viewport?, timeout?, wait?, instructions?, model? }` — no `port` field. Default `path` is `/`; default `name` comes from the path (`/login` → `login`, `/` → `root`). `name` matches `[a-z0-9_-]+`; reusing a name in the same attempt fails. `viewport` is `{ width, height }` or `'full-page'` (default window, tall saved PNG). `wait` is `'load'` (default), `'idle'` (Chromium `networkAlmostIdle`), or `{ ms }`, once before the first model shot or the saved PNG. `instructions` is a journey of goal strings and `{ wait }` beats (a `{ wait: 'idle' }` beat waits from that point until at most two requests have been in flight for 500ms); each string is a vision loop (pixels → click / type / key / scroll / drag / hover, no selectors). `model` overrides cascaded `capture.model`; without either, `analysis.model`, else the attempt's judge model when it's a language model. Tests drive the journey with `fakeInstructions` from `{{ pkg }}/testing` (not a `fake` key). The same `timeout` is the budget: on timeout there is no PNG. `capture.browser: false` or `REEVAL_CAPTURE_BROWSER=0` throws. See [Capturing UI](/recipes/capturing-ui).
</Property>

<Property name="readFile" type="(path: string) => Promise<string | null>">
  Reads a file from the sandbox: a workspace-relative path, or `~/` for the home directory. Resolves with `null` when the file doesn't exist.
</Property>

<Property name="writeFile" type="(path: string, contents: string | Uint8Array) => Promise<void>">
  Writes a file into the sandbox as the sandbox user, creating its directories: a workspace-relative path, or `~/` for the home directory. Later turns' `diff` leaves the file out, as it does the environment's files, but shows any change made to it afterwards, even under the workspace's `.gitignore`. Use it to place a hidden test after a turn, so the agent never sees it, or seed a file before the first `c.send`: nothing in the sandbox keeps what the file was before, so the agent can't recover a seeded change from reeval's snapshot (a workspace with its own `.git`, such as a `workspace` with `init: true`, still shows it in `git diff` unless you commit it there too). Each write is a line in the debug trace and nothing in the record. An absolute path or one that leaves the workspace throws.
</Property>

<Property name="judge" type="(question: string, options?: JudgeCallOptions) => Promise<Verdict>">
  Asks the judge a yes/no question about the latest turn: by default its task, final message, changed files and diff. `options` takes `model`, `threshold`, `providerOptions`, `maxConcurrent` and `state` (what the judge is given, such as `{ transcript: 'tools' }`) for this call, `on` (a `Turn` or any JSON value to grade instead), and `artifacts` (capture handles or names, sent as file parts). Without a turn it throws `c.judge() has no turn to grade: call c.send() first`. See [Judges](/reference/judges).
</Property>

## Retrying a command

```ts title="evals/flow.eval.ts"
await expect(
  c.exec('npx tsx --test hidden/flow.test.ts', { retries: 2, interval: 2000 }),
  'flow works',
).toSucceed()
```

A check that calls a model or a network service can fail on a transient that isn't the agent's fault. `retries` runs the command again on any non-zero exit; reeval doesn't read its output to decide. The check passes if any try exits 0, and a last failed try is a failed check, as without `retries`.

The record keeps each try on the check (`checks[].tries`), and its log has each try's exit and output. The output, `summary.md`, `reeval show` and the studio say how it ended:

```text
fail             fails
  always fails     failed
    expected command to succeed, exit code 3
    failed all 3 tries
pass             lands
  flaky passes     passed on try 2 of 3
```

`retries` is a whole number from 0, and `interval` is 0 or more milliseconds. Neither goes with `background: true`, whose exit reeval doesn't wait for.

## Properties

<Property name="turns" type="readonly Turn[]">
  Every turn so far, oldest first.
</Property>

<Property name="signal" type="AbortSignal">
  Aborted when the eval times out or you press Ctrl-C. Pass it to long-running work so it stops with the attempt.
</Property>

<Property name="attempt" type="{ id: string, cell: Cell, n: number }">
  Which attempt this is. `id` is `<cell key>#<n>`; `n` counts from 1. `cell` has `key` (`harness/environment/sandbox`), `harness`, `environment` (`-` for none), `sandbox`, `role` (`control` or `variant`) and, for a variant, `variant`.
</Property>

A fake run printing `c.attempt` shows:

```text
{"id":"fake/-/fake#1","n":1,"cell":{"key":"fake/-/fake","harness":"fake","environment":"-","sandbox":"fake","role":"control"},…}
```

## Turn

`c.send()` resolves with a frozen `Turn`.

<Property name="prompt" type="string">
  The prompt you sent.
</Property>

<Property name="text" type="string">
  The agent's final message for this turn.
</Property>

<Property name="status" type="'completed' | 'aborted' | 'error'">
  How the turn ended. `aborted` means the attempt timed out or was interrupted; `error` means the harness reported an error, which is also in the transcript.
</Property>

<Property name="startedAt" type="string">
  When the turn started, as an ISO timestamp.
</Property>

<Property name="durationMs" type="number">
  How long the turn took.
</Property>

<Property name="transcript" type="TranscriptEvent[]">
  What the agent did this turn: text, reasoning, file edits, tool calls, web searches and fetches, and errors. See [Transcripts](/reference/transcript).
</Property>

<Property name="usage" type="{ input, output, cacheRead, cacheWrite, reasoning } | null">
  Tokens the turn used, when the harness reports them (`cursor()`, `claudeCode()`, `antigravity()`, `opencode()`, `pi()`, `codex()`, `fx()` and `grokBuild()` do; `goose()` doesn't); otherwise `null`. See [Transcripts](/reference/transcript#tokens-and-the-served-model).
</Property>

<Property name="model" type="string | null">
  The model the harness says served the turn, such as `composer-2.5[fast=false]`, or `null`.
</Property>

<Property name="unreported" type="Array<'web-search' | 'web-fetch'>">
  Web event types the harness couldn't report for this turn, so the transcript
  may be missing them. Usually empty. `toHaveSearched` and `toHaveFetched`
  throw `HarnessCapabilityError` on a turn that lists theirs. See
  [Matchers](/reference/matchers#tohavesearched-and-tohavefetched).
</Property>

<Property name="diff" type="{ patch: string, files: { path, status }[] }">
  Workspace changes since the eval started, as of the end of this turn. `status` is `added`, `modified` or `deleted`. Files, rules and setup from the environment aren't part of it, and nor is a file `c.writeFile` placed until something changes it.
</Property>

<Property name="exec" type="(command: string, options?: { retries?: number, interval?: number }) => Promise<ExecResult>">
  The same as `c.exec`.
</Property>

<Property name="readFile" type="(path: string) => Promise<string | null>">
  The same as `c.readFile`.
</Property>

## Verdict

`c.judge()` resolves with a `Verdict`:

| Field | Type | Meaning |
|---|---|---|
| `verdict` | `'pass' \| 'fail'` | `pass` when `score` is at least `threshold` |
| `score` | `number` | The judge's score, from 0 to 1 |
| `threshold` | `number` | The threshold used, default 0.8 |
| `question` | `string` | The question asked |
| `model` | `string` | The model setting, as written |
| `resolvedModel` | `string` | The model that answered, when the provider says |
| `artifacts` | `JudgeArtifactRef[]` | Captures this call sent as file parts, when any |

A `Verdict` doesn't fail the eval on its own: pass it to `expect(…).toPass()`.

A call that fails resolves with a `JudgeFailure` instead: `verdict: 'error'`, `score: null` and `error`, the call's message. It fails the attempt as a judge error once the eval's body is done; see [Failed calls](/reference/judges#failed-calls).

## Multi-turn

Each `c.send()` continues the same session. Checks between turns see the workspace as the agent left it:

```ts title="evals/refactor.eval.ts"
evaluate('adds then renames', async (c) => {
  await c.send('Create greet.js exporting greet(name).')
  const second = await c.send('Rename greet to welcome.')
  await expect(second).toHaveFile('greet.js', 'welcome')
  expect(c.turns).toHaveLength(2)
})
```

## Related

- [Writing evals](/guide/writing-evals)
- [Multi-turn evals](/guide/multi-turn)
- [Matchers](/reference/matchers)
- [Judges](/reference/judges)
