---
title: fx()
description: >-
  The fx harness, its options and id, its AI Gateway or OpenAI key, the relay
  that takes it through the egress gateway, and what that relay can't cover.
---
> ## 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.

`fx()` runs Vercel's [fx](https://fx.sh) in the Docker sandbox over its ACP server (`fx acp`), on Vercel AI Gateway or OpenAI's API. `model` is required, and its form picks the provider.

```ts title="reeval.config.ts"
import { defineConfig } from '{{ pkg }}'
import { direct, vercel } from '{{ pkg }}/gateway'
import { fx } from '{{ pkg }}/harness'

export default defineConfig({
  harness: fx({ model: 'anthropic/claude-sonnet-5' }),
  gateway: [vercel(), direct()],
})
```

A gateway id runs only through [`vercel()`](/reference/gateway#vercel-ai-gateway), so the config lists it in `gateway`; a bare OpenAI id runs on `direct()`, or on Vercel as `openai/<id>` when `vercel()` comes first.

## Options

<Property name="model" type="string">
  A Vercel AI Gateway id with its provider, such as `anthropic/claude-sonnet-5`, which runs on AI Gateway through `vercel()` in the config's `gateway`, or a bare OpenAI id, such as `gpt-5.5`, which runs on OpenAI's Chat Completions API. Required.
</Property>

<Property name="id" type="string" optional>
  The harness's id in cell keys, filters and the record. Default `fx:<model>`.
</Property>

<Property name="gateway" type="'direct' | Gateway" optional>
  The gateway this harness's model calls go through, by name or helper, such as `direct()`. It skips the config's `gateway` fallback: the plan refuses a gateway that isn't listed or can't serve this harness. See [Gateways](/reference/gateway#pinning-a-harness).
</Property>

<Property name="maxConcurrent" type="number" optional>
  The most fx attempts at once across the run, for example for your key's rate limits.
</Property>

<Property name="startTimeout" type="Duration" optional>
  How long its session may take to become ready (bootstrap and session start) before the attempt is an infra error, such as `'3m'`. Default `'90s'`. A start that times out is aborted and started once more; a second timeout fails the attempt with `fx: session did not become ready within 90s (tried twice)`, and the record's `startRetries` notes a retried start.
</Property>

`fx()` has no `effort`: fx offers a reasoning level only on reasoning models, and its OpenAI route can't run those (below).

## At a glance

| | |
|---|---|
| Import | `'{{ pkg }}/harness'` |
| Credential | `vercel()`'s (`AI_GATEWAY_API_KEY`) on Vercel, `OPENAI_API_KEY` for a bare OpenAI id on `direct()` |
| Adapter | `@reeval/harness-fx` 0.1.1 (over `@ai-sdk/harness-acp`), bundled with reeval |
| CLI | fx 0.0.14, from its GitHub release, checked against its sha256 |
| Sandbox | `docker()` only |
| Image | `reeval-sandbox:fx-0.1.1` |
| Rules file | `AGENTS.md` |
| Skills (default placement) | `.fx/skills` in fx's own home (below) |
| Skills (`placement: 'workspace'`) | `.fx/skills` in the workspace |
| Egress hosts added | `ai-gateway.vercel.sh` or `api.openai.com`, by the model |
| Tokens | Reported for each turn |
| Served model | From each API response, which the gateway reads |
| MCP | `mcp.stdio`, `mcp.http` and `mcp.sse`, over ACP |
| Plugins | Skills and MCP servers ([below](#plugins)) |

Build its image once with `reeval image build` in a project whose config uses `fx()`. `gpt-5.5` passes the harness contract on OpenAI; pin the model you mean to measure.

## The key and the relay

```text
fx ──http──▶ relay on 127.0.0.1:47823 ──HTTPS_PROXY──▶ egress gateway ──▶ provider
   placeholder key                                     real key swapped in
```

fx's HTTP client ignores `HTTPS_PROXY`, so it can't reach the egress gateway itself. The adapter points fx at a small Node relay inside the sandbox, which forwards each request through the gateway with the sandbox's proxy and CA. fx gets a placeholder; the gateway swaps the real key into the `Authorization` header on these requests only:

| `model` | Host | Endpoint |
|---|---|---|
| Gateway id | `ai-gateway.vercel.sh` | `GET /coding-agent/v1/…` (the model list) and `POST /v<n>/ai/language-model` |
| Bare OpenAI id | `api.openai.com` | `POST /v1/chat/completions` |

The adapter refuses to run in a sandbox that can't broker the key.

<Warning>
  Only model calls go through the relay. fx's own web search and fetch, and its HTTP and SSE MCP servers, use its HTTP client: a restricted egress policy blocks them, and the record lists none of their requests. Commands the agent runs honour the proxy as usual.
</Warning>

## Models and tokens

| `model` | Runs on | Served model recorded |
|---|---|---|
| `gpt-5.5` | An fx configured provider, `openai`, over the Chat Completions API | `gpt-5.5-2026-04-23` |
| `anthropic/claude-sonnet-5` | fx's `gateway` provider | The model AI Gateway answers with; none when its stream names none, with the model each call asked for in `askedModels` |

fx sends a configured provider no `reasoning_effort` and no `max_tokens`, so a bare id must be a model that accepts function tools on Chat Completions without `reasoning_effort`, such as `gpt-5.5`. OpenAI refuses them from `gpt-5.6` on unless `reasoning_effort` is `none`, so run those through AI Gateway. Each turn's tokens are fx's ACP `PromptResponse.usage`, the turn's summed input and output, with its reasoning tokens. fx doesn't name the served model over ACP, so the gateway reads it from each response; a provider whose stream names none (Gemini's) records no served model, and the attempt's `askedModels` holds the model each successful call asked AI Gateway for, its `ai-language-model-id` header. A turn whose every model call got an HTTP error fails, naming the statuses. Session titles are off, since fx writes them with its own model, so the run's model is the only one called.

## Settings

| Setting | Why |
|---|---|
| `FX_PROVIDER`, `FX_MODEL` | The provider the model picks, and the model the session starts on. |
| `FX_AUTO_UPGRADE=0`, `FX_SOUND=0`, `NO_COLOR=1` | No self-upgrade, sounds or colour. |
| `full-access` session mode | Tool calls run without asking; fx's `auto` mode would send each reviewed action to a reviewer model. |

fx runs with a private home, `~/.ai-sdk-harness/.harness-bootstrap/fx/implementation/home`, so its settings and home skills live there. The attempt's `context` lists them under that path.

fx names its tool calls, so the transcript reads `bash`, `write`, `edit`, `read`, `glob`, `grep`, `webSearch` and `webFetch`. A command's output is rebuilt from what it streamed, since fx's final update carries a clipped summary. MCP calls are renamed from `mcp_<server>_<tool>` to `mcp__<server>__<tool>`, and a `skill` call names the skill it loaded. fx's `subagent` tool runs a task in a subagent whose own tool calls aren't in fx's stream, so they aren't in the transcript.

## Plugins

```ts title="evals/env.ts"
export const house = defineEnvironment('house', ({ plugin }) => ({
  plugins: [plugin.local('../plugin', { only: ['skills', 'mcp'] })],
}))
```

fx has no plugins, no subagents of your own and no commands, so it loads a plugin's skills and MCP servers only. Its skills go in fx's home `.fx/skills`, with the environment's own, and its servers over ACP as `<plugin>:<server>`. A plugin's `agents/`, `commands/`, `hooks/` or `rules/` exit 2 at plan time; `only` leaves them out.

## Errors

| Situation | Message |
|---|---|
| No model | `fx() needs a model: a Vercel AI Gateway id, e.g. fx({ model: 'anthropic/claude-sonnet-5' }), or a bare OpenAI id, e.g. fx({ model: 'gpt-5.5' }) (got …)` |
| A gateway id without `vercel()` in `gateway` (at plan time, exit 2) | `evaluate("<eval>") on fx:<model>: no gateway in the list can serve it (direct: fx runs a gateway id such as "anthropic/claude-sonnet-5" only through Vercel AI Gateway). Add vercel() to gateway.` |
| No key when the attempt starts | `AI_GATEWAY_API_KEY isn't set, so the fx harness can't authenticate. Set it in the environment or .env.local.` (or `OPENAI_API_KEY`) |
| The provider refuses the key | `The API rejected OPENAI_API_KEY: set a valid key in the environment or .env.local. …` (or `AI_GATEWAY_API_KEY`) |
| A plugin with subagents, commands, hooks or rules (at plan time, exit 2) | `evaluate("<eval>") on fx:<model>: fx can't load the <plugin> plugin's agents (agents/): use only: ['skills', 'mcp'], or leave the plugin out of this cell` |
| A non-Docker sandbox | `The fx harness needs the Docker sandbox: set sandbox: docker() …` |
| fx doesn't start | `fx harness failed to start: …` |

## Related

- [Harnesses](/guide/harnesses)
- [Secrets and egress](/guide/secrets-and-egress)
- [Transcripts](/reference/transcript)
