Pre-Tool Hooks: Put Your AI Agent's Rules in Code, Not in the Prompt
There is a complaint going around agent circles right now: the agent tells you what it is about to do, then does something slightly different. Or it promises to follow the rule, then quietly doesn’t.
This is usually treated as a model problem. It is mostly a design problem. You asked a probabilistic system to enforce a deterministic rule, and you used English to do it.
A prompt is a request, a hook is a gate
Every rule you put in a system prompt is a polite request. “Never delete files outside the workspace.” “Always ask before sending an email.” “Don’t push to main.” Most of the time the model complies. Some of the time, under a long context, a confusing tool result, or a cleverly worded piece of untrusted text, it doesn’t.
A pre-tool hook is different. It is a small piece of code that runs between the model deciding to call a tool and the tool actually running. The model proposes. The hook disposes. If the call breaks a rule, it never executes, and the model gets a plain refusal it can react to.
That is the whole idea. The rule moves from the part of the system that can be talked out of it to the part that cannot.
What a hook sees
A good hook receives three things:
- The tool name, such as
exec,send_email, orwrite_file. - The arguments, exactly as the model produced them.
- Context, such as which session, which job, whether a human is present, and how many calls have happened so far.
It returns one of three verdicts: allow, deny with a reason, or ask a human. Three outcomes are enough. Resist the urge to build a policy language before you have written five hooks by hand.
Five hooks worth writing first
You do not need a big framework. These five cover most of the damage unattended agents actually do.
1. Path fence
Deny any file write or delete whose resolved path falls outside an allowlist of directories. Resolve symlinks and .. before checking, because the model will never think to, but a prompt-injected string might. This one hook prevents the “it cleaned up the wrong folder” class of incident.
2. Command denylist with an allowlist underneath
For shell tools, deny patterns like rm -rf, git push --force, git reset --hard, and curl | sh. Then, for unattended jobs, go further: allow only a short list of known commands and deny everything else by default. A denylist catches the mistakes you thought of. An allowlist catches the ones you didn’t.
3. Branch and environment guard
If a job is supposed to deploy from main, the hook checks the current branch and refuses otherwise. If a job should never touch production credentials, the hook refuses any call whose arguments mention them. The agent does not need to remember the rule. The rule is simply true.
4. Rate and volume cap
Count calls per tool per run. Cap emails at ten, API writes at fifty, deploys at one. When the cap trips, deny and log. This is the cheapest protection against loops, which are the most common way an agent hurts you without ever being malicious.
5. Human-approval tripwire
Some actions should always pause for a person: anything that sends money, posts publicly, emails a customer, or deletes data. The hook returns “ask,” the run parks, and nothing happens until someone approves. In an unattended job where nobody can approve, “ask” should resolve to deny, never to allow.
Make denials useful to the model
A bare “blocked” makes agents flail. They retry, rephrase, or try a sideways route to the same outcome.
Write denial messages like a good code reviewer: say what was blocked, why, and what is allowed instead.
Denied: write to /etc/hosts. Writes are limited to /home/user/project. Use a path inside the project directory.
A specific denial usually produces a specific correction on the next turn. A vague one produces a retry storm.
Log every verdict
Every hook decision, allow or deny, should land in a log with a timestamp, tool, arguments, and verdict. This gives you two things.
First, an audit trail. When something odd happens, you can see exactly what the agent attempted, not what it said it attempted.
Second, a tuning signal. If a hook denies the same legitimate call twenty times a week, the rule is wrong. If it has never fired in a month, either the rule is unnecessary or your agent is better behaved than you feared. Both are worth knowing.
This also closes the “says one thing, does another” gap. The log records what actually ran. Compare the agent’s report against the log, and discrepancies stop being a matter of trust.
Do not turn hooks into a second prompt
The failure mode of hooks is bloat. Someone adds a hook for every anxiety, and soon the agent can barely act. Keep these principles:
- Hooks enforce facts, not preferences. “Never write outside the workspace” is a fact. “Prefer concise commit messages” is a preference and belongs in the prompt.
- Fail closed for destructive actions, fail open for reads. If the hook crashes, a delete should not proceed. A file read probably should.
- Keep them boring. A hook should be readable in under a minute. If you cannot explain what it blocks in one sentence, split it.
- Test them. A hook that has never been fed a bad call is a hook you are hoping works. Write three or four deliberately bad inputs per hook and keep them as a test.
Where this fits with the rest of your stack
Hooks do not replace good prompts, scoped credentials, or backups. They sit between them. Scoped credentials limit what the agent could possibly do. Hooks limit what it is allowed to do right now. The prompt shapes what it wants to do. You want all three, but if you only have time for one safety investment this week, make it the layer that cannot be argued with.
The takeaway
If a rule matters, do not ask the model to follow it. Make it impossible to break.
Start with the path fence and the branch guard, because they are ten lines each and prevent the most embarrassing mistakes. Add the rate cap next. Log everything. Then watch the log for a week before you write a sixth hook.
An agent that you can only trust when it behaves is a demo. An agent whose worst day is bounded by code is something you can leave running overnight.
More from the build log
Suggested
Want the full MarketMai stack?
Get the core MarketMai guides and operator playbooks in one premium bundle for $49.
View Bundle