Hooks
Write sandboxed agent-qa hooks that prepare data, verify side effects, and export runtime variables back to tests and suites.
Hooks are project scripts that agent-qa runs in a Docker sandbox. Use them when a test needs setup data, API verification, cleanup, or a runtime value that should be captured before a browser or mobile step runs.
The full field reference lives in Hook. This guide focuses on writing one hook and using it from tests or suites.
Register a hook
workspace.hooksFile points agent-qa at a hook registry, usually hooks.yaml.
workspace:
hooksFile: hooks.yamlThe hook registry contains a hooks list. A hook needs an id, name, runtime, file, and timeout. network defaults to true; set it explicitly when the hook calls an external or local API.
hooks:
- id: h_aster-bloom-cloud-drift-ember-field-glade-hollow-ivory-jasper
name: Fetch first Hacker News story
runtime: node
file: scripts/fetch-hn-top-story.mjs
timeout: 30s
network: trueSupported runtimes are node, bun, python, and bash.
Fetch the Hacker News top story
agent-qa init currently generates the Node version of this Hacker News hook for web projects. The Bun, Python, and Bash examples below implement the same hook contract: fetch top stories, validate the first story and title, then write HN_FIRST_STORY_TITLE and HN_FIRST_STORY_ID to /tmp/agent-qa.env.
// scripts/fetch-hn-top-story.mjs
import { writeFile } from "node:fs/promises"
const topStoriesUrl = "https://hacker-news.firebaseio.com/v0/topstories.json"
async function getJson(url) {
const response = await fetch(url)
if (!response.ok) {
throw new Error(`HN API request failed: ${response.status} ${response.statusText}`)
}
return response.json()
}
function escapeEnvValue(value) {
return String(value)
.replace(/\r?\n/g, " ")
.replace(/\\/g, "\\\\")
.replace(/"/g, '\\"')
}
const storyIds = await getJson(topStoriesUrl)
const firstStoryId = Array.isArray(storyIds) ? storyIds[0] : undefined
if (!Number.isInteger(firstStoryId)) {
throw new Error("HN API returned no first story id")
}
const story = await getJson(`https://hacker-news.firebaseio.com/v0/item/${firstStoryId}.json`)
const title = typeof story?.title === "string" ? story.title.trim() : ""
if (!title) {
throw new Error(`HN item ${firstStoryId} returned no title`)
}
await writeFile("/tmp/agent-qa.env", [
`HN_FIRST_STORY_TITLE="${escapeEnvValue(title)}"`,
`HN_FIRST_STORY_ID=${firstStoryId}`,
"",
].join("\n"), "utf-8")Use hooks from tests and suites
Use setup when the hook should run before the test or suite starts.
setup:
- h_aster-bloom-cloud-drift-ember-field-glade-hollow-ivory-jasper
steps:
- Navigate to "https://news.ycombinator.com/"
- Verify the page shows "{{env:HN_FIRST_STORY_TITLE}}"Use teardown for cleanup hooks that should run after the test or suite.
teardown:
- h_update-borg-artha-any-packet-derive-torch-front-plied-bedUse inline runHook syntax when the hook belongs at one specific step.
steps:
- Run the HN lookup hook {{runHook:"h_aster-bloom-cloud-drift-ember-field-glade-hollow-ivory-jasper"}}.
- Verify the first story id is "{{env:HN_FIRST_STORY_ID}}".Inline hooks run before variable interpolation for that step. Variables exported by the hook are available to later steps.
Export variables
To return values to agent-qa, write a dotenv file at /tmp/agent-qa.env.
HN_FIRST_STORY_TITLE="Launch HN: Example"
HN_FIRST_STORY_ID=123456After the container exits, agent-qa reads that file and merges the variables into the run. A later successful hook can override a variable exported by an earlier hook.
Add dependencies when needed
deps copies extra files into the sandbox beside the hook entry file. packageFile copies a package file when the runtime needs package metadata.
hooks:
- id: h_aster-bloom-cloud-drift-ember-field-glade-hollow-ivory-jasper
name: Fetch first Hacker News story
runtime: node
file: scripts/fetch-hn-top-story.mjs
deps:
- scripts/hn-client.mjs
packageFile: package.json
timeout: 30s
network: trueKeep dependencies small. Files are copied into the temporary workspace for the hook run, not mounted from the repository.
Read active web auth state
Authenticated web runs can expose the selected auth state to setup, inline, and teardown hooks. See Auth state for the AGENT_QA_AUTH_STATE_JSON and AGENT_QA_AUTH_STATE_STORAGE_STATE_PATH contract, plus Bash, Python, Node, and Bun examples that parse the raw storage-state JSON without Playwright.
Sandbox environment
agent-qa runs hooks with Docker using the runtime image for the hook.
- The hook entry file,
deps, and optionalpackageFileare copied into a temporary host directory. - That directory is mounted at
/workspace, and the hook command runs with/workspaceas the working directory. - The container filesystem is read-only.
/tmpis writable and is where/tmp/agent-qa.envis written.- Resource limits are applied with
--memory 512m,--cpus 1, and--pids-limit 256. - Environment variables from env files, CLI variables, previous successful hooks, and configured secrets are injected into the container.
- Known secret values are redacted from hook stdout, stderr, and errors. Variables written to
/tmp/agent-qa.envare filtered when their value exactly matches a known secret.
Network access is enabled by default. Set network: false when the hook must not call the network.
hooks:
- id: h_aster-bloom-cloud-drift-ember-field-glade-hollow-ivory-jasper
name: Validate exported HN variables offline
runtime: bash
file: scripts/validate-hn-env.sh
timeout: 10s
network: falseWhen network: false is set, agent-qa passes --network none to Docker for that hook container.
Runtime images
The current hook runner images are published under the vostride Docker Hub namespace: