Overview
Details
Features
- Policy-gated tool calls — a refused call never reaches the tool, and the model is told which rule refused it
- Once stays once — an effect key executes at most once, across processes sharing a store
- Unknown outcomes stay unknown — a tool that raises leaves the effect unresolved rather than retried
- Human approval — a policy decision of
approveholds the call for a person - A receipt for every decision — requests, decisions and results, refusals included
Setup
No account and no API key. CTRLRun is a library, and the policy is a file in your repository.Installation
Write a policy
ctrlrun.yaml says how much autonomy each tool gets. Unknown tools are denied; there is no default-allow.
Instantiation
Invocation
ctrlrun.context(...) supplies it in development; in production an identity provider verifies a credential instead.
What the agent sees
The middleware useswrap_tool_call, so a refused call is short-circuited — the tool is never invoked, and the model receives a ToolMessage explaining why:
handler is the executor, the effect is reserved before the tool runs and committed from what it returned. Two agents sharing a store cannot both execute the same effect key, and a tool that raises leaves the outcome AMBIGUOUS rather than FAILED — so the retry is refused until a person resolves it, instead of becoming a double charge.
Approvals
Where the policy saysapprove, the call is held and the model is told how to release it:
ctrlrun-langgraph, which routes the approval through LangGraph’s interrupt() and re-presents the same proposal on resume.
Next
- Use the LangGraph adapter: approvals answered inside the run, through
interrupt(). - Adapters: the three ways in, and why this one is prevention.
- The middleware’s README · Get started · Why.