Agent runtime controls (hooks and checkpoints)

What are agent runtime controls?

Agent runtime controls are the checks that the software around an agent runs at fixed moments during a run, in code, whether the model agrees or not. Two mechanisms do most of the work.

A hook is code the runtime calls at a defined point, for example just before a tool call goes out. It looks at what is about to happen and answers one question: let it through, change the arguments, ask a person, or refuse. A checkpoint is a saved copy of the state from before a change, so you can put things back.

The distinction that makes the term worth having is between a request and an execution. Write "never touch the production database" in a system prompt and you have an instruction the model usually follows. Put the same rule in a hook and the call does not go out, whatever the model concluded. Guardrails is the wider word for every protective layer around an AI application; this entry is about the layer that is code.

Where a hook fires, and what it can do there

Names differ per runtime. Claude Code calls them hook events, the Microsoft Agent Framework calls the same idea middleware, and the points are the same.

  • Before a tool call. The most useful point. Claude Code names it PreToolUse and lets a hook answer allow, deny or requireApproval, and rewrite the arguments through updatedInput. So it can block the call, strip a customer name out of what is about to be sent, or push the call to a human decision.

  • On a permission decision. A hook can answer instead of the person, through a decision object with approved and a reason, so the yes or no comes from your policy code rather than from someone clicking a prompt at eleven at night.

  • After a tool result. The action already happened, so nothing gets blocked. A hook reads the result and feeds something back into the run: the tests are red, the query returned 40,000 rows.

  • Before the run ends. A hook can refuse the ending and send the agent back to work, which turns "the linter has to pass first" into a rule instead of a hope.

  • On session start and stop. Load whatever context the run needs, and write an audit line at the end.

Microsoft's framework has the same shape with other words: to block something you simply do not call the next step, by setting context.result and raising MiddlewareTermination.

A hook that blocks one action

Take an operations agent that cleans up supplier records. It may read the ERP, it may write a corrected address, and it may never run anything against the production database. A hook on the shell tool checks each command for a connection string pointing at production and answers with this:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Production writes go through the change process. Put the statement in a file and ask a person to run it."
  }
}

What happens next is the part people underestimate. The agent does not crash. It reads the reason and finishes the task by another route: it writes the statement to a file, names that file in its summary and asks you to run it. The wording of the reason is doing real work, because it is the only thing telling the model which path is acceptable. A bare "denied" gets you an agent that tries four variations of the same forbidden command first.

Checkpoints, and what they cannot undo

Checkpoints work after the fact rather than before it. Claude Code captures the state of your files before each prompt you send, keeps snapshots for the 100 most recent checkpoints in a session, and removes them with the session after 30 days. Cursor creates one before significant agent changes.

The limits are the useful part. Claude Code does not track files modified by bash commands, so an rm or an mv the agent ran is not undone by a rewind, and edits by a background subagent are usually not restored either. Cursor puts it plainly: restoring reverts files only. A checkpoint restores state you own, not the world.

A mail that went out, a payment that was posted, a row written into a supplier's system through an API: none of that comes back. Files and local state are covered by checkpoints, and anything that leaves the building has to be covered by a hook before the action, because afterwards there is nothing left to protect.

Read and propose as the default setting

For most businesses the sane starting position fits in one sentence: the agent may read anything inside its scope and may change nothing without a person confirming the diff.

Runtimes support that directly. Claude Code has a plan mode in which the agent reads files and runs read-only shell commands to explore, but does not edit your source files. A run then ends in a proposal you approve in one place, with the whole thing in front of you, instead of fifteen separate prompts you stop reading around number six.

Cost and approval fit the same slot. A budget check is a hook: count the spend during the run and stop the loop at the ceiling, rather than letting the invoice do it a month later. An approval is a hook on the mail tool, the payment tool and the delete tool while the rest runs on. The real autonomy you have handed an agent is not what your policy document says, it is which of its actions still pass through a hook.

A hook versus a line in the system prompt

Both express the same rule. They differ on one dimension: what happens when the model decides otherwise.

An instruction in the system prompt is read together with everything else the model sees, including the ticket text, the fetched web page and the PDF a customer sent. Those inputs can carry instructions of their own, which is the prompt injection problem, and OWASP is direct about it: "it is unclear if there are fool-proof methods of prevention for prompt injection". Their advice is to handle privileged functions in code instead of giving them to the model. When the model decides otherwise, an instruction produces nothing. No event, no log line, no block.

A hook is not part of the conversation, so it cannot be talked out of its answer. The call is refused, the refusal is logged, and the model finds out as a tool result. That is why one is a control and the other is a suggestion. The prompt is still the right place to say what the agent should do, and the wrong place for the handful of rules you cannot afford to have broken.

What to watch out for with runtime controls

A hook only covers what it matches. Hooks are wired to tool names, so connect one new MCP server and the agent has tools your matcher never heard of, running unguarded. Every time the tool list grows, the coverage question comes back.

Shell access goes around almost everything. An agent that can run commands has many routes to the same effect: curl instead of the mail tool, a Python script instead of the delete tool. Blocking one command name is not blocking the capability, which is the argument for an agent sandbox underneath the hooks.

Know which way a check fails. In Claude Code a hook that times out before a tool call does not block that call, so a slow check quietly becomes no check.

Hooks do not outrank permission rules. Claude Code applies deny and ask rules regardless of what a hook returned, so a matching deny rule blocks the call even when the hook answered allow. Put the absolute prohibitions there, where a broken hook script cannot open them. And a hook is code you own, running with your credentials, so review it like the rest of your code.

Last Updated: September 4, 2026 Back to Dictionary
Keywords
agent runtime controls hooks checkpoints agent guardrails human in the loop approval workflow least privilege agent sandbox prompt injection ai agent agentic ai ai governance