Pre-execution validation for AI agent tool calls

6 min read

Pre-execution validation checks an AI agent’s tool call after the model proposes it and before anything happens: are the arguments well formed, is this agent allowed to do this, and does this action need a person to approve it. A call that fails is refused, so the side effect never happens and there is nothing to clean up the next morning.

In a typical agent loop, the model emits a tool call, the framework parses it, and the matching function runs. Anything in between is code you add. The AEGIS authors (below) describe most current agent stacks the same way: tool calls reach execution with no control point in between.

Three kinds of check, cheapest first

Check What it catches Example rule Typical cost
Schema Hallucinated or malformed arguments amount must be a positive integer; path is required Negligible; no I/O
Policy Actions the agent may not take, or arguments outside its limits Writes only under /srv/app/; refunds up to 200 EUR; production database read-only One policy lookup
Approval Actions that are allowed but too consequential to run unattended Any payment above a threshold; any deletion in production A person’s time, so use sparingly

Run them in that order and stop at the first failure. A schema failure should go back to the model with a structured error so it can retry. A policy failure should not: retrying a forbidden action with different wording is how an injected prompt works its way through. An approval failure is a third case. The call is allowed in principle, so the run pauses until a person answers.

Flynn, the reference runtime described below, makes the same distinction in its error classes. A policy denial is classed forbidden and must not be retried or escalated; an action that needs a person is classed needs_approval, which pauses the run instead of failing it.

The checks need not be slow. One research prototype, AEGIS (arXiv 2603.12621, March 2026), runs argument extraction, content risk scanning and policy checks on every call, and its authors report a median overhead of 8.3 ms across 1,000 interceptions, with all 48 attacks in their test suite blocked and a 1.2% false-positive rate on 500 benign calls. Those are the authors’ figures on their own suite. AEGIS also writes its decisions to an audit trail signed with Ed25519 and hash-chained.

The check belongs in the dispatcher

There are two places to put these rules. Put them in the system prompt and they are advice: the model usually follows them, and a prompt injection can talk it out of them. Put them in the code that executes a proposed call and they are enforcement: the call cannot reach the tool without passing.

The dispatcher is also the only place that sees every call. A check inside one tool covers that tool. A check at dispatch covers the tools you add next month as well.

A refusal with no record cannot be shown to anyone

Suppose an auditor asks what stops your agent from moving money it should not. Showing them the policy file proves you wrote a rule. It does not show that the rule ran.

What shows it is a record, written by the dispatcher, of every call admitted and every call refused, signed so that an edit afterwards is detectable. Provetrail, a draft specification at 0.1.0, defines that record. In its conformance vectors, and in Flynn, each governed call emits dispatch.start when admitted, dispatch.end when it completes, or dispatch.rejected when it is refused, all under one correlation id. A verifier that checks the governance level (L4) then enforces two rules over the signed record:

  • No action completed without an earlier admission (gov.unadmitted_action).
  • No action was both refused and completed (gov.admission_denied_but_executed).

A record that passes both, with refusals in it, shows that the gate fired on those calls and that nothing it refused went on to run. It does not yet show that a gate covered every action. The spec plans a check for records that carry no governance events at all (shallow.empty_governance), and it is not part of conformance at this version. The steps to produce such a record are in how to produce tamper-proof evidence of AI agent actions, and what a verifier checks is in how to verify an AI agent’s execution record.

Flynn runs the gate and writes the record in one place

In Flynn, the reference implementation of Provetrail, every governed action passes through one dispatcher, model calls and MCP calls included. Before an action runs, the dispatcher applies a chain of checks:

  • Brakes: a kill switch, an action-rate limit, and a breaker that trips on the same action repeated.
  • Budget: the run’s spending limit, refused with budget_exceeded.
  • Approval: a quorum of signed approvals bound to the exact action and scope. No approval pauses the run; an invalid one is a refusal.
  • Capability: the run’s grant, a list of the actions it may take. A child run gets a grant narrowed from its parent’s, never a wider one.

A refused action never runs, and the refusal goes into the record as dispatch.rejected with a stable error code, so the record names the rule that refused the call. One default to know: with no grant bound to the run, the capability check admits every action, so bind one.

The grant is addressed by action name. Argument limits, such as the 200 EUR refund ceiling in the table above, belong in the tool or in a policy hook you register, and Flynn records that hook’s refusals the same way.

To see what a signed record looks like and check one yourself, the home page verifies one in your browser.