- A project created with
lua initand signed in withlua auth configure(Install and sign in).
1
Write a preprocessor
execute receives the end user, the message as an array of parts, and the channel. Return { action: 'proceed' }, optionally with modifiedMessage for the model to read instead, or { action: 'block', response } to end the turn with response as the reply. Lower priority runs first; the default is 100. channel is the inbound name from channels: pop for the web widget (the type lists it as web, so compare against both), whatsapp, facebook for Messenger, email, sms; lua chat sends dev. These differ from the Channels.send names (webchat, messenger).src/preprocessors/PiiGuard.ts
2
Write a postprocessor
execute receives the end user, the original message, the model’s reply, and the channel, and must return { modifiedResponse }; that text replaces the reply.src/postprocessors/WhatsAppFooter.ts
3
Register both on the agent
Only processors referenced from
LuaAgent are compiled.src/index.ts
4
Test them locally
lua test runs one processor on your machine with the input you give it; no model is involved.Output
Output
lua chat -e sandbox -m "reach me at [email protected]": sandbox chat uploads your processors as sandbox versions and runs them on the platform, with no push.5
Release them
lua push uploads a version and changes nothing for end users; lua version create snapshots the agent; lua version promote <n> makes that snapshot live and is also the rollback path (Release an agent to production).lua version create prints ✓ Created v<n> (staged); in a script, n=$(lua version list --limit 1 --json --ci | jq -r '.[0].version') then lua version promote "$n". lua push all exits 0 even when a primitive fails; check its output for component(s) failed to push.6
Verify
Send a message the guard should block, then read the log.The reply is the guard’s
response, word for word, and the log shows the blocked turn. lua logs --type postprocessor --limit 5 shows the footer being applied to WhatsApp replies.Options you may need
Order several processors
Active processors of each kind run in ascendingpriority, each receiving the previous one’s output. Two processors with the same priority run in the order the database returns them, which you cannot set, so give each a distinct number.
Deploy one processor on its own
lua deploy preprocessor and lua deploy postprocessor are single-primitive shortcuts: each creates and promotes an agent version scoped to that processor, so it goes live immediately and appears in lua version list.
Block silently
response on a block is what the end user sees, and an empty string is honored differently per client: the legacy stream shows nothing, the widget shows the default text User not eligible for agent response, and a non-streaming chat call returns preprocessor_blocked with empty text. Set response whenever the end user should know why.
If it isn’t working
To turn a processor off in production without a rollback, runlua preprocessors deactivate --preprocessor-name pii-guard --ci (or lua postprocessors deactivate --postprocessor-name whatsapp-footer --ci); activate turns it back on.
No preprocessors found in compiled output.
No preprocessors found in compiled output.
Cause
lua test only sees processors registered on LuaAgent.preProcessors or postProcessors. Fix Import the processor in src/index.ts, add it to the right array, and run the test again.The message reached the model unchanged
The message reached the model unchanged
Cause The preprocessor threw or timed out, so the platform skipped it and continued; or another preprocessor with a lower
priority blocked or replaced the message first. Fix Read lua logs --type preprocessor --limit 5 for the error, and compare the priority values of your preprocessors in code.Next steps
About processors
Order, failure handling, streaming, and processors versus skills.
PreProcessor reference
Config fields and the result shape.
PostProcessor reference
Config fields and the response shape.
Add human handoff
A blocking preprocessor driven by a flag on the end user.

