Checking agent actions before they run

Adding guardrails to an agent usually starts in its prompt. Something like "ask before changing anything in production" or "don't delete the things you didn't create". The agent follows those instructions most of the time, so the prompt seems to work until one day it forgets a rule.

Instructions in the prompt

The agent reads an instruction along with everything else in the conversation, and sometimes it acts against it anyway. In July 2025, a Replit agent deleted a production database during a code freeze, after being told not to make changes without approval.

Instructions can also disappear. When a long conversation is compacted, the summary can leave a rule out. In February 2026, an OpenClaw agent lost its instruction to confirm before acting this way and started deleting emails.

For simple agents, prompt instructions may be enough. As you give an agent access to more tools and data, you need a more reliable way to enforce its permissions.

Checks in agent code

Agent frameworks like LangChain, AI SDK, Mastra, and Pydantic AI all support tool approvals in agent code. You mark a tool as needing approval, and each call to it waits until someone approves or rejects it.

This works well for a single agent you manage yourself. With more agents, especially ones built by different teams, the rules become harder to keep track of. Finding an agent's permissions means reading its code, and changing a hardcoded rule requires a redeploy.

Permissions outside the agent

You can move the rules into a policy file outside the agent. Before a tool runs, the agent submits the tool name and arguments for a permission check. The policy decides whether the call can run, needs approval, or is denied.

Agents built by different teams or with different frameworks can go through the same check. You can see their permissions in the policies and change a rule without editing or redeploying the agents.

Tool calls pass through a permission check before reaching external systems.

Tool calls pass through a permission check before reaching external systems.

Checking the arguments

Requiring approval for a tool works well until that tool is something generic like bash. Then git status needs the same approval as terraform apply. If most commands are routine, approving each one becomes a chore.

Checking the arguments lets you allow specific commands, block others, and ask for approval for the rest.

Every command goes through the same bash tool. The arguments decide which ones need approval.

Every command goes through the same bash tool. The arguments decide which ones need approval.

An example policy

In Rebuno, these rules go in the agent's policy. Each tool call is recorded as a step, and the kernel checks it against the policy before it runs.

Here's a policy for an agent with a bash tool. Three commands run without asking, commands containing rm -rf are denied, and every other bash command waits for approval.

default_action: denyrules:  - id: allow-safe    when:      target: bash      arguments:        command:          one_of: ["git status", "git diff", "npm test"]    then:      decision: allow   - id: no-rm-rf    when:      target: bash      arguments:        command:          contains: rm -rf    then:      decision: deny      reason: commands containing rm -rf are blocked   - id: approve-other    when:      target: bash    then:      decision: require_approval      reason: other commands need approval

Rules are checked in order they are written, and the first match decides. git status matches the allow-safe rule and runs right away. rm -rf data/ matches no-rm-rf and is denied. terraform apply doesn't match with the first two rules so it falls through to approve-other.

If you later decide the agent can run git log without asking, you add it to the list in allow-safe. The next git log runs without approval, with no changes to the agent's code.

To learn more about Rebuno, read the docs or visit the repository on GitHub.