Skip to main content
E2EConfig is the type of the object e2e.config.ts default-exports. Write the object literal and end it with satisfies E2EConfig: the editor completes every key, unknown keys are flagged, and the export keeps its literal type. Every rule below is enforced when the runner resolves the config, not by the type.
e2e.config.ts
Unknown keys are rejected with INVALID_CONFIG at every level. There is no top-level app key: the engine declares the app it drives (see the app under test).

Loading

--config <path> selects an explicit file. Otherwise the runner searches the working directory and each parent for e2e.config.ts or e2e.config.mts, stopping at the repository root. Both names in one directory is CONFIG_AMBIGUOUS. The selected file’s directory is the project root. Config and test modules run as ESM. e2e loads the .ts, .mts, and .tsx files a project reaches by path as ES modules whatever the nearest package.json type says, so a CommonJS package keeps its module type. Packages imported by name keep the format their own manifest declares. A config or test written with require or module.exports fails to load.

Top level

Every numeric value must be a safe integer.

targets

{ engine: playwright({ url }) } is a complete target. Two targets on one platform name themselves. A target without an engine runs opaquely through agent tools; the runner cannot observe or drive it through an engine. Ordinary tests whose requires list names a capability the target lacks are skipped for that target. A test.setup needed by a selected session consumer must be runnable there; if its required capabilities are missing, selection fails with COLLECTION_ERROR. Calling an unsupported engine operation or acquiring a fixture it does not contribute fails with UNSUPPORTED_CAPABILITY.

The app under test

The app is declared by the engine that drives it. The browser engine takes the declaration as options of playwright(...); the device engine derives it from the app it pins. The runner resolves it at config load and owns what is built on it: navigation and origin policy, cache and session identity, the report’s target record, and the app process.

command

The child inherits only PATH, HOME, TMPDIR, TMP, TEMP, SystemRoot, and COMSPEC. Model credentials, E2E_USER_* values, and CI tokens are not inherited automatically. Every value explicitly supplied in command.env is forwarded, including a credential or token. The runner never terminates a process it did not start.

services

Exactly one of readyUrl or waitForExit is required per service. reuseExisting works on a readyUrl service and skips its teardown; on a waitForExit service it is INVALID_CONFIG. Services start in declaration order before any app command, each ready before the next starts. Services declared identically by several targets start once; two targets that order shared services differently are INVALID_CONFIG. On every exit path the runner stops every app command, then the services in reverse order, then runs their teardown commands in reverse order. A failing teardown is recorded as a cleanup run error and never skips the teardowns after it. Starting your app shows a full example.

artifacts

kinds accepts screenshot, trace, and video. The default set is screenshot and trace, captured best-effort. A configured kind the target’s engine cannot produce fails before collection with UNSUPPORTED_ARTIFACT. video is never in the default set; --video adds it for one run. store receives every artifact the moment it is complete on disk and returns its own reference, which the report records as the artifact’s ref beside the local path. A failed put never fails the run. A store never crosses a process boundary: each worker constructs its own from the config module.

video

With video among the kinds, or --video, every selected engine must support recording; otherwise configuration fails with UNSUPPORTED_ARTIFACT. Playwright opens a page when recording starts and writes WebM segments, one per page it records. The agent-device engine writes an MP4 of the device screen. Other engines define their recording format and when segments exist. A failed launch or a recording that cannot be finalized may leave no video. The first file lands under the attempt’s artifact directory as video/video.webm for Playwright or video/video.mp4 for agent-device. Playwright names later segments video/video-part<n>.webm. Each segment has startedAt set to when recording began, so step.startedAt - artifact.startedAt is a step’s offset into that segment. Recording costs an encoder per page and about a megabit per second of disk. A recording masks nothing, so a video artifact is recorded with redaction: 'incomplete'. Turning video on or off never invalidates cached replays.

screen

agents

Agents by name. default is the one tests run with, and it exists even when the config names none. Other names are other brains for the same suite: e2e run --agent <name> re-points the default, a test or describe block pins one with { agent: 'name' } or several with { agent: ['buyer', 'admin'] }, and any agent.* call can name one. An unknown name fails by where it was written: --agent is INVALID_CONFIG, a pin is COLLECTION_ERROR, a call’s agent is INVALID_ARGUMENT. Names are ASCII letters, numbers, _, -, or .. See Agents and personas.
e2e.config.ts
Each agent takes three shapes: an options object, an options object with an executor, or the executor itself. The bare form is shorthand for { executor }. Every diagnostic names the agent: agents.ux.model. Judgments (assert, waitFor, extract) allow 8192 output tokens per call and set no temperature.

model

The model is always an AI SDK model instance the config constructs. Detection is structural, so any provider package works without the runner depending on it, and the instance owns its transport and credentials. Reports record the instance’s provider and modelId as the step’s provider and model. Each worker re-resolves the config module and constructs its own instance. A config whose digest differs between the runner and a worker fails with CONFIG_NOT_DETERMINISTIC. See Choosing a model.

credentials

credentials.user(name) resolves a configured entry into a Credential. The username is a plain string; the password is an opaque Secret that test code cannot read, accepted only by locator.fill and agent.act params. An unconfigured name throws AUTH_CREDENTIAL_UNAVAILABLE.
See Signing in.

cache

In read-write mode, the trace cache records an agent.act() only after a later recorded verification passes, such as a locator assertion or an agent.assert() step. A trailing act with no verification is not recorded. read-only mode can replay existing entries but writes none; off disables both. Caching agent steps explains verification and when a replay hands off.

Commit your traces

e2e init adds .e2e/cache/ to .gitignore, so committing entries is opt-in. Remove that line to share replays with CI and teammates; CI stays read-only unless cache: 'read-write' is set. See Caching agent steps.

limits

Resolved limits are immutable for a run. Per-call options may only lower them. A key exists here exactly when the runner has an enforcement site for it.

Environment variables

The runner reads a closed set, listed on the Environment variables page. The ones that change what a config resolves to: <NAME> is the credential name uppercased with every character outside A-Z0-9 replaced by _, so power-user becomes E2E_USER_POWER_USER_USERNAME. A pair for a name absent from credentials is ignored.

Resolution order

A missing model is checked once per run, when the agent fixture is first acquired, and stops the run with exit 2. A custom executor with no model skips the check.

CI

CI mode is on unless CI is unset, empty, whitespace only, 0, or false (trimmed and lowercased first). Every other value turns it on.

Starting your app

Continuous integration

CLI

Errors