Skip to main content
Skip to Content

Some things are faster and more reliable outside the UI: creating a fresh account, seeding an order, reading a database row, cleaning up afterwards. Hooks are your own scripts that agent-qa runs at fixed points in a test, in a Docker sandbox.

Hooks need Docker running. npx agent-qa doctor checks it in its Docker row.

Define a hook

Hooks live in hooks.yaml, the file workspace.hooksFile names:

hooks:
  - id: h_phi-arts-vis-band-aira-streng-twig-roi-mino-fold
    name: Make a shipping address
    runtime: node
    file: scripts/examples/make-shipping-address.mjs
    timeout: 30s
    network: false

runtime is node, bun, python or bash. The hooks file reference lists every field. New hooks get their ID from the dashboard's hook editor or from your coding agent through agent_qa_change_config.

Pass values to the test

A hook hands values to the test by writing KEY=value lines to /tmp/agent-qa.env. Here is the example init writes, and the same hook in each runtime:

// scripts/examples/make-shipping-address.mjs
import { writeFile } from 'node:fs/promises'
import { randomInt } from 'node:crypto'

const address = `${randomInt(100, 1000)} Cedar Lane`
await writeFile('/tmp/agent-qa.env', `SHIPPING_ADDRESS="${address}"\n`, 'utf-8')

The test reads them as {{env:KEY}}:

setup:
  - h_phi-arts-vis-band-aira-streng-twig-roi-mino-fold
steps:
  - Type {{env:SHIPPING_ADDRESS}} into the Street address field, then continue to delivery.

When hooks run

WhereWhen
setup: in a testBefore the first step. If it fails, the test fails.
teardown: in a testAfter the last step, whether the test passed or not. A failing teardown hook does not change the verdict.
setup: and teardown: in a suiteOnce around the whole suite. Values from a suite setup hook reach every test.
{{runHook:"h_…"}} in a stepIn the middle of the test, before that step runs. Use it to check a side effect, then verify the value in the next step.

Setup hooks run in order. When one fails, the rest are skipped and the test, or the suite, fails.

The sandbox

Each hook runs in a fresh container from vostride/agent-qa-hook-runner-<runtime>. The first run pulls the image, so it takes longer.

  • 512 MB of memory, one CPU and up to 256 processes, on a read-only file system.
  • A working folder at /workspace that holds only the hook file, the files listed in deps and the packageFile, side by side. Your project is not mounted.
  • /tmp is writable.
  • No network when network: false. Without the field, the hook has network access.
  • Nothing is installed. A hook can use its runtime's standard library and the files you copy in, so bundle any packages it needs into those files.
  • A hook that runs past its timeout fails.

The container receives the run's variables, the secrets from your secrets file except those a remote, plugin or device uses to connect, and the active saved login when there is one. Values a hook writes that match a secret are dropped, and its output is masked like everything else.

Example: check a side effect

steps:
  - Place the order and note the order number shown.
  - Look up the order through the API {{runHook:"h_shy-oak-tide-rim-ash-fog-ivy-elm-bay-ore"}}.
  - Verify ORDER_FOUND is exactly "true"; the current value is "{{env:ORDER_FOUND}}".

The hook queries your API and writes ORDER_FOUND=true or false. The next step checks it, so a backend bug fails the test even when the UI looks right.