Skip to main content
After this guide, you can prove a change works at every rung of the ladder: the function alone, the agent in the sandbox, and the live agent after a release. Each rung is one command that needs no prompt, so it runs the same from your shell, a script, or a coding agent. For the release itself, see Release an agent to production. Verified against lua-cli 3.33.0. Before you begin
  • A project created with lua init and signed in with lua auth configure (Install and sign in). Local runs still need a credential and the project’s agent ID.
  • Sandbox secrets in .env, set with lua env sandbox -k <KEY> -v <value>. lua test loads .env into the run; see the lua env reference.
1

Run one tool with exact input

lua test skill compiles the project, then calls the tool’s execute function directly in a local runtime. No model is involved, so the output is exactly what the tool returns. --name is the tool name, not the skill name, and --input is the tool’s own input fields.
Output
Platform calls inside the tool (Data, User, Channels) go to the real API under your credential. A tool that throws does not fail the command: the run still prints ✅ Tool execution successful!, exits 0, and shows { status: 'error', error: '<message>' } as the output, so check the output rather than the exit code (with --json: jq -e 'type != "object" or .status != "error"'). A run that ends with Tool "<name>" not found means you passed the skill name; run lua test skill with no flags to pick from the compiled tool list.
2

Run a webhook, job, or processor the same way

The other lua test types take the shape their primitive receives. A webhook gets query, headers, and body; a job takes no input; a preprocessor gets message and channel; a postprocessor gets message, response, and channel.
Output
A preprocessor run prints the action and the processed messages (Action: PROCEED, then 1. [TEXT] Call me on [phone] or mail [email]); a postprocessor run prints Processed response: followed by the modified text. The webhook’s secret is not checked here: the platform verifies signatures only on incoming requests.
3

Talk to the agent in the sandbox

lua chat -e sandbox compiles your project, uploads the compiled skills and processors as sandbox versions, and sends your persona with each message; the platform runs them against the model, so no lua push is needed and production is untouched. Pass -t with no value to get a fresh, isolated thread whose ID is printed as ℹ️ Thread: <id>; add --clear to delete that history when the command exits.
Without -t, the message lands in your default thread and earlier turns shape the answer. Without -e, lua chat -m defaults to the sandbox and says so. Sandbox chat reads the same .env as lua test: lua env sandbox -k <KEY> -v <value> writes only that file, there is no server-side sandbox variable store, and lua chat -e sandbox uploads the file’s values with the sandbox versions it compiles, so your tools run on the platform with them. Production reads the variables set with lua env production.
4

Run a workflow offline

lua test workflow drives a workflow locally on a virtual clock. Agent steps are faked by default (--agents fake answers [fake:<stepId>] … plus a schema-shaped object); tool steps need --step-output <stepId>=<json> or recorded --fixtures <dir>; approvals are pre-answered with --approve <stepId> or --deny <stepId>.
Code steps are not faked: they execute your TypeScript, and platform calls inside them (AI.generate, Channels.send, Data) run for real against the API. Pass --agents live to call the model for agent steps too. The complete driver is on Test a workflow offline.
5

Run voice tests

lua voice test runs every *.voice.test.ts file in the project with Jest or Vitest, whichever the project has installed; --voice <name> limits the run to <name>.voice.test.ts.
The command exits non-zero when a test fails or when neither runner is installed. Test helpers are described on the lua voice reference.
6

Verify production after a release

Once a version is live, send one message to production on a fresh thread, then confirm that no agent_error entry and no skill entry with subType error were written; a throwing tool is logged under skill. The time-bounded form of this check is in Release an agent to production.
Output
An empty logs array is the pass condition. Anything else: read the entries with Read logs and debug an agent.

Options you may need

Script the output

Add --json to any lua test type to get the raw return value on stdout with progress on stderr, so lua test skill --name lookup_tickets --input '{"customerEmail":"[email protected]"}' --json | jq length works. lua logs --json returns { logs, pagination }.

Let a coding agent run the ladder

The Claude Code plugin’s /lua-qa command runs compile, lua test, and a sandbox conversation, then reports what failed; see Claude Code plugin.

If it isn’t working

Exit code 3. --name received a skill name or a misspelled tool name. Tool names are the name property of the class, for example lookup_tickets; run lua test skill without flags to see the compiled list.
Exit code 2. The --input string is not valid JSON. Wrap it in single quotes so the shell leaves the double quotes alone, and pass the tool’s fields directly, without a {"tool": …} envelope.
lua voice test needs Jest or Vitest in the project. Install one (npm install -D vitest) and re-run; --runner jest or --runner vitest forces the choice.

Next steps

Release an agent to production

Push, version, promote, verify, and roll back.

Read logs and debug an agent

Filter lua logs and run the debug loop.

lua test reference

Every type, flag, and exit code.

About environments

What the sandbox shares with production.