{"id":3506,"date":"2026-07-10T04:11:27","date_gmt":"2026-07-10T04:11:27","guid":{"rendered":"https:\/\/tucumandevelopers.com\/index.php\/2026\/07\/10\/ai-agent-runtime-policy-stop-dangerous-tool-calls-before-they-execute\/"},"modified":"2026-07-10T04:11:27","modified_gmt":"2026-07-10T04:11:27","slug":"ai-agent-runtime-policy-stop-dangerous-tool-calls-before-they-execute","status":"publish","type":"post","link":"https:\/\/tucumandevelopers.com\/index.php\/2026\/07\/10\/ai-agent-runtime-policy-stop-dangerous-tool-calls-before-they-execute\/","title":{"rendered":"AI Agent Runtime Policy: Stop Dangerous Tool Calls Before They Execute"},"content":{"rendered":"<div>\n<div><\/div>\n<p>A normal permission check near the API endpoint is still required, but it does not answer everything:<\/p>\n<ul>\n<li>Should the agent attempt this action at all?<\/li>\n<li>Does the action match the user&#8217;s request?<\/li>\n<li>Is the target tenant correct?<\/li>\n<li>Is the cost acceptable?<\/li>\n<li>Does it require approval?<\/li>\n<li>Is the agent stuck in a retry loop?<\/li>\n<\/ul>\n<p>Runtime policy answers those questions before execution.<\/p>\n<h2> <a name=\"what-existing-ai-security-content-often-misses\" href=\"#what-existing-ai-security-content-often-misses\"> <\/a> What existing AI security content often misses <\/h2>\n<p>A lot of content explains prompt injection, RAG risks, or broad AI governance. Useful, but builders often need a narrower answer:<\/p>\n<blockquote>\n<p>&#8220;My agent is about to call a tool. What should my app check before that call runs?&#8221;<\/p>\n<\/blockquote>\n<p>The practical gap is <strong>agent tool call authorization<\/strong>, <strong>runtime AI policy enforcement<\/strong>, <strong>agent permissions<\/strong>, <strong>spend caps<\/strong>, and <strong>approval gates<\/strong> in one implementation pattern.<\/p>\n<h2> <a name=\"runtime-policy-vs-prompt-guardrails\" href=\"#runtime-policy-vs-prompt-guardrails\"> <\/a> Runtime policy vs prompt guardrails <\/h2>\n<p>Prompt guardrails guide the model. Runtime policy enforces what the system allows.<\/p>\n<div>\n<table>\n<thead>\n<tr>\n<th>Layer<\/th>\n<th>What it does<\/th>\n<th>Weakness<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>System prompt<\/td>\n<td>Guides behavior<\/td>\n<td>Can be ignored or manipulated<\/td>\n<\/tr>\n<tr>\n<td>Tool descriptions<\/td>\n<td>Explain actions<\/td>\n<td>Often too broad<\/td>\n<\/tr>\n<tr>\n<td>App authorization<\/td>\n<td>Checks final API access<\/td>\n<td>May miss intent, autonomy, and cost<\/td>\n<\/tr>\n<tr>\n<td>Runtime policy<\/td>\n<td>Evaluates proposed actions before execution<\/td>\n<td>Requires explicit rules<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>Use prompts to shape behavior. Use runtime policy to enforce boundaries.<\/p>\n<h2> <a name=\"the-core-architecture\" href=\"#the-core-architecture\"> <\/a> The core architecture <\/h2>\n<p>Put one gate between the agent and every tool. <\/p>\n<div>\n<pre><code>Agent orchestrator -&gt; proposed tool call -&gt; runtime policy engine -&gt; allow \/ deny \/ hold \/ modify -&gt; tool executor -&gt; app API, database, queue, or external API <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>The policy engine should return one of four decisions:<\/p>\n<ul>\n<li> <code>allow<\/code>: run the tool call.<\/li>\n<li> <code>deny<\/code>: block it and return a safe explanation.<\/li>\n<li> <code>hold<\/code>: pause for human approval.<\/li>\n<li> <code>modify<\/code>: reduce scope, mask fields, or route to a safer tool.<\/li>\n<\/ul>\n<p>The agent never calls production tools directly. It requests execution through the gate.<\/p>\n<h2> <a name=\"start-with-an-action-envelope\" href=\"#start-with-an-action-envelope\"> <\/a> Start with an action envelope <\/h2>\n<p>Do not pass raw tool calls straight into your executor. Wrap every proposed action in a server-owned object. <\/p>\n<div>\n<pre><code><span>type<\/span> <span>AgentActionEnvelope<\/span> <span>=<\/span> <span>{<\/span> <span>action_id<\/span><span>:<\/span> <span>string<\/span><span>;<\/span> <span>tenant_id<\/span><span>:<\/span> <span>string<\/span><span>;<\/span> <span>user_id<\/span><span>:<\/span> <span>string<\/span><span>;<\/span> <span>agent_id<\/span><span>:<\/span> <span>string<\/span><span>;<\/span> <span>session_id<\/span><span>:<\/span> <span>string<\/span><span>;<\/span> <span>task_id<\/span><span>:<\/span> <span>string<\/span><span>;<\/span> <span>tool_name<\/span><span>:<\/span> <span>string<\/span><span>;<\/span> <span>tool_version<\/span><span>:<\/span> <span>string<\/span><span>;<\/span> <span>operation<\/span><span>:<\/span> <span>\"<\/span><span>read<\/span><span>\"<\/span> <span>|<\/span> <span>\"<\/span><span>write<\/span><span>\"<\/span> <span>|<\/span> <span>\"<\/span><span>send<\/span><span>\"<\/span> <span>|<\/span> <span>\"<\/span><span>delete<\/span><span>\"<\/span> <span>|<\/span> <span>\"<\/span><span>execute<\/span><span>\"<\/span><span>;<\/span> <span>target_resource<\/span><span>:<\/span> <span>string<\/span><span>;<\/span> <span>arguments<\/span><span>:<\/span> <span>Record<\/span><span>&lt;<\/span><span>string<\/span><span>,<\/span> <span>unknown<\/span><span>&gt;<\/span><span>;<\/span> <span>user_intent<\/span><span>:<\/span> <span>string<\/span><span>;<\/span> <span>autonomy_mode<\/span><span>:<\/span> <span>\"<\/span><span>draft<\/span><span>\"<\/span> <span>|<\/span> <span>\"<\/span><span>copilot<\/span><span>\"<\/span> <span>|<\/span> <span>\"<\/span><span>autopilot<\/span><span>\"<\/span><span>;<\/span> <span>environment<\/span><span>:<\/span> <span>\"<\/span><span>dev<\/span><span>\"<\/span> <span>|<\/span> <span>\"<\/span><span>staging<\/span><span>\"<\/span> <span>|<\/span> <span>\"<\/span><span>production<\/span><span>\"<\/span><span>;<\/span> <span>estimated_cost_cents<\/span><span>?:<\/span> <span>number<\/span><span>;<\/span> <span>risk_flags<\/span><span>?:<\/span> <span>string<\/span><span>[];<\/span> <span>created_at<\/span><span>:<\/span> <span>string<\/span><span>;<\/span> <span>};<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>The model should not decide the security context. Your app creates it.<\/p>\n<p>A good envelope gives policy code enough information to answer: who, which tenant, which tool, which operation, which resource, which mode, which environment, and what cost.<\/p>\n<h2> <a name=\"define-risk-tiers\" href=\"#define-risk-tiers\"> <\/a> Define risk tiers <\/h2>\n<p>Tool access should not be a simple yes\/no flag.<\/p>\n<div>\n<table>\n<thead>\n<tr>\n<th>Tier<\/th>\n<th>Examples<\/th>\n<th>Default decision<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>0: Safe read<\/td>\n<td>Search docs, summarize public pages<\/td>\n<td>Allow with logging<\/td>\n<\/tr>\n<tr>\n<td>1: Tenant read<\/td>\n<td>Read tickets, invoices, internal notes<\/td>\n<td>Allow if scoped<\/td>\n<\/tr>\n<tr>\n<td>2: Draft write<\/td>\n<td>Draft reply, prepare report<\/td>\n<td>Allow or hold by mode<\/td>\n<\/tr>\n<tr>\n<td>3: Reversible write<\/td>\n<td>Add comment, update tag<\/td>\n<td>Allow if scoped<\/td>\n<\/tr>\n<tr>\n<td>4: External side effect<\/td>\n<td>Send email, call webhook, invite user<\/td>\n<td>Hold unless delegated<\/td>\n<\/tr>\n<tr>\n<td>5: Destructive or privileged<\/td>\n<td>Delete data, change roles, run production SQL<\/td>\n<td>Deny or require strong approval<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>A single tool may expose multiple tiers. For example, a CRM tool can read accounts, update fields, merge contacts, and delete records. Treat each operation separately.<\/p>\n<h2> <a name=\"build-a-simple-policy-function\" href=\"#build-a-simple-policy-function\"> <\/a> Build a simple policy function <\/h2>\n<p>You can start with code. You do not need a heavy rules engine on day one. <\/p>\n<div>\n<pre><code><span>type<\/span> <span>PolicyDecision<\/span> <span>=<\/span> <span>{<\/span> <span>decision<\/span><span>:<\/span> <span>\"<\/span><span>allow<\/span><span>\"<\/span> <span>|<\/span> <span>\"<\/span><span>deny<\/span><span>\"<\/span> <span>|<\/span> <span>\"<\/span><span>hold<\/span><span>\"<\/span> <span>|<\/span> <span>\"<\/span><span>modify<\/span><span>\"<\/span><span>;<\/span> <span>reason<\/span><span>:<\/span> <span>string<\/span><span>;<\/span> <span>required_approval_role<\/span><span>?:<\/span> <span>\"<\/span><span>owner<\/span><span>\"<\/span> <span>|<\/span> <span>\"<\/span><span>admin<\/span><span>\"<\/span> <span>|<\/span> <span>\"<\/span><span>operator<\/span><span>\"<\/span><span>;<\/span> <span>policy_ids<\/span><span>:<\/span> <span>string<\/span><span>[];<\/span> <span>};<\/span> <span>function<\/span> <span>evaluatePolicy<\/span><span>(<\/span><span>action<\/span><span>:<\/span> <span>AgentActionEnvelope<\/span><span>):<\/span> <span>PolicyDecision<\/span> <span>{<\/span> <span>if <\/span><span>(<\/span><span>action<\/span><span>.<\/span><span>environment<\/span> <span>===<\/span> <span>\"<\/span><span>production<\/span><span>\"<\/span> <span>&amp;&amp;<\/span> <span>action<\/span><span>.<\/span><span>operation<\/span> <span>===<\/span> <span>\"<\/span><span>delete<\/span><span>\"<\/span><span>)<\/span> <span>{<\/span> <span>return<\/span> <span>{<\/span> <span>decision<\/span><span>:<\/span> <span>\"<\/span><span>deny<\/span><span>\"<\/span><span>,<\/span> <span>reason<\/span><span>:<\/span> <span>\"<\/span><span>Agents cannot delete production resources.<\/span><span>\"<\/span><span>,<\/span> <span>policy_ids<\/span><span>:<\/span> <span>[<\/span><span>\"<\/span><span>prod-delete-deny<\/span><span>\"<\/span><span>]<\/span> <span>};<\/span> <span>}<\/span> <span>if <\/span><span>(<\/span><span>action<\/span><span>.<\/span><span>operation<\/span> <span>===<\/span> <span>\"<\/span><span>send<\/span><span>\"<\/span> <span>&amp;&amp;<\/span> <span>action<\/span><span>.<\/span><span>autonomy_mode<\/span> <span>!==<\/span> <span>\"<\/span><span>autopilot<\/span><span>\"<\/span><span>)<\/span> <span>{<\/span> <span>return<\/span> <span>{<\/span> <span>decision<\/span><span>:<\/span> <span>\"<\/span><span>hold<\/span><span>\"<\/span><span>,<\/span> <span>reason<\/span><span>:<\/span> <span>\"<\/span><span>External sends require approval.<\/span><span>\"<\/span><span>,<\/span> <span>required_approval_role<\/span><span>:<\/span> <span>\"<\/span><span>operator<\/span><span>\"<\/span><span>,<\/span> <span>policy_ids<\/span><span>:<\/span> <span>[<\/span><span>\"<\/span><span>send-requires-approval<\/span><span>\"<\/span><span>]<\/span> <span>};<\/span> <span>}<\/span> <span>if <\/span><span>((<\/span><span>action<\/span><span>.<\/span><span>estimated_cost_cents<\/span> <span>??<\/span> <span>0<\/span><span>)<\/span> <span>&gt;<\/span> <span>50<\/span><span>)<\/span> <span>{<\/span> <span>return<\/span> <span>{<\/span> <span>decision<\/span><span>:<\/span> <span>\"<\/span><span>hold<\/span><span>\"<\/span><span>,<\/span> <span>reason<\/span><span>:<\/span> <span>\"<\/span><span>Estimated cost exceeds the per-action budget.<\/span><span>\"<\/span><span>,<\/span> <span>required_approval_role<\/span><span>:<\/span> <span>\"<\/span><span>admin<\/span><span>\"<\/span><span>,<\/span> <span>policy_ids<\/span><span>:<\/span> <span>[<\/span><span>\"<\/span><span>cost-threshold-hold<\/span><span>\"<\/span><span>]<\/span> <span>};<\/span> <span>}<\/span> <span>if <\/span><span>(<\/span><span>action<\/span><span>.<\/span><span>risk_flags<\/span><span>?.<\/span><span>includes<\/span><span>(<\/span><span>\"<\/span><span>contains_secret<\/span><span>\"<\/span><span>))<\/span> <span>{<\/span> <span>return<\/span> <span>{<\/span> <span>decision<\/span><span>:<\/span> <span>\"<\/span><span>deny<\/span><span>\"<\/span><span>,<\/span> <span>reason<\/span><span>:<\/span> <span>\"<\/span><span>Tool arguments contain a detected secret.<\/span><span>\"<\/span><span>,<\/span> <span>policy_ids<\/span><span>:<\/span> <span>[<\/span><span>\"<\/span><span>secret-egress-deny<\/span><span>\"<\/span><span>]<\/span> <span>};<\/span> <span>}<\/span> <span>return<\/span> <span>{<\/span> <span>decision<\/span><span>:<\/span> <span>\"<\/span><span>allow<\/span><span>\"<\/span><span>,<\/span> <span>reason<\/span><span>:<\/span> <span>\"<\/span><span>Action passed runtime policy checks.<\/span><span>\"<\/span><span>,<\/span> <span>policy_ids<\/span><span>:<\/span> <span>[<\/span><span>\"<\/span><span>default-allow-scoped<\/span><span>\"<\/span><span>]<\/span> <span>};<\/span> <span>}<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>Keep this boring. Policy should be deterministic, testable, and easy to explain during an incident review.<\/p>\n<h2> <a name=\"check-delegation-not-only-user-permissions\" href=\"#check-delegation-not-only-user-permissions\"> <\/a> Check delegation, not only user permissions <\/h2>\n<p>A user may be an admin. That does not mean every agent running under that user should get admin power.<\/p>\n<p>Use a delegation object: <\/p>\n<div>\n<pre><code><span>{<\/span><span> <\/span><span>\"delegation_id\"<\/span><span>:<\/span><span> <\/span><span>\"del_123\"<\/span><span>,<\/span><span> <\/span><span>\"tenant_id\"<\/span><span>:<\/span><span> <\/span><span>\"tenant_abc\"<\/span><span>,<\/span><span> <\/span><span>\"user_id\"<\/span><span>:<\/span><span> <\/span><span>\"user_456\"<\/span><span>,<\/span><span> <\/span><span>\"agent_id\"<\/span><span>:<\/span><span> <\/span><span>\"support_triage_agent\"<\/span><span>,<\/span><span> <\/span><span>\"allowed_tools\"<\/span><span>:<\/span><span> <\/span><span>[<\/span><span>\"ticket.search\"<\/span><span>,<\/span><span> <\/span><span>\"ticket.comment\"<\/span><span>,<\/span><span> <\/span><span>\"kb.search\"<\/span><span>],<\/span><span> <\/span><span>\"allowed_operations\"<\/span><span>:<\/span><span> <\/span><span>[<\/span><span>\"read\"<\/span><span>,<\/span><span> <\/span><span>\"draft\"<\/span><span>,<\/span><span> <\/span><span>\"comment\"<\/span><span>],<\/span><span> <\/span><span>\"denied_operations\"<\/span><span>:<\/span><span> <\/span><span>[<\/span><span>\"delete\"<\/span><span>,<\/span><span> <\/span><span>\"send_refund\"<\/span><span>,<\/span><span> <\/span><span>\"change_role\"<\/span><span>],<\/span><span> <\/span><span>\"max_cost_cents\"<\/span><span>:<\/span><span> <\/span><span>200<\/span><span>,<\/span><span> <\/span><span>\"expires_at\"<\/span><span>:<\/span><span> <\/span><span>\"2026-07-10T10:30:00Z\"<\/span><span> <\/span><span>}<\/span><span> <\/span><\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>Enforce narrowing:<\/p>\n<ul>\n<li>A sub-agent can receive fewer permissions, never more.<\/li>\n<li>A workflow step can receive fewer tools than the whole task.<\/li>\n<li>A delegation expires quickly.<\/li>\n<li>Revocation stops future calls immediately.<\/li>\n<\/ul>\n<p>This is similar to OAuth scopes, but it must cover actions, resources, cost, and time windows.<\/p>\n<h2> <a name=\"validate-tool-arguments\" href=\"#validate-tool-arguments\"> <\/a> Validate tool arguments <\/h2>\n<p>Models produce plausible arguments. Plausible is not safe.<\/p>\n<p>Use schemas and business rules before execution. <\/p>\n<div>\n<pre><code><span>import<\/span> <span>{<\/span> <span>z<\/span> <span>}<\/span> <span>from<\/span> <span>\"<\/span><span>zod<\/span><span>\"<\/span><span>;<\/span> <span>const<\/span> <span>SendEmailArgs<\/span> <span>=<\/span> <span>z<\/span><span>.<\/span><span>object<\/span><span>({<\/span> <span>to<\/span><span>:<\/span> <span>z<\/span><span>.<\/span><span>string<\/span><span>().<\/span><span>email<\/span><span>(),<\/span> <span>subject<\/span><span>:<\/span> <span>z<\/span><span>.<\/span><span>string<\/span><span>().<\/span><span>min<\/span><span>(<\/span><span>3<\/span><span>).<\/span><span>max<\/span><span>(<\/span><span>120<\/span><span>),<\/span> <span>body<\/span><span>:<\/span> <span>z<\/span><span>.<\/span><span>string<\/span><span>().<\/span><span>min<\/span><span>(<\/span><span>10<\/span><span>).<\/span><span>max<\/span><span>(<\/span><span>5000<\/span><span>),<\/span> <span>customer_id<\/span><span>:<\/span> <span>z<\/span><span>.<\/span><span>string<\/span><span>().<\/span><span>startsWith<\/span><span>(<\/span><span>\"<\/span><span>cus_<\/span><span>\"<\/span><span>),<\/span> <span>attachments<\/span><span>:<\/span> <span>z<\/span><span>.<\/span><span>array<\/span><span>(<\/span><span>z<\/span><span>.<\/span><span>string<\/span><span>()).<\/span><span>max<\/span><span>(<\/span><span>3<\/span><span>).<\/span><span>default<\/span><span>([])<\/span> <span>});<\/span> <span>function<\/span> <span>validateSendEmailArgs<\/span><span>(<\/span><span>args<\/span><span>:<\/span> <span>unknown<\/span><span>)<\/span> <span>{<\/span> <span>return<\/span> <span>SendEmailArgs<\/span><span>.<\/span><span>parse<\/span><span>(<\/span><span>args<\/span><span>);<\/span> <span>}<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>Then add server-side checks:<\/p>\n<ul>\n<li>The customer belongs to the current tenant.<\/li>\n<li>The recipient domain is allowed.<\/li>\n<li>The message contains no secrets.<\/li>\n<li>Attachments are visible to the user.<\/li>\n<li>The action matches the current task.<\/li>\n<\/ul>\n<p>Argument validation catches both model mistakes and prompt-injection fallout.<\/p>\n<h2> <a name=\"match-action-to-intent\" href=\"#match-action-to-intent\"> <\/a> Match action to intent <\/h2>\n<p>A common agent failure is action drift.<\/p>\n<p>The user asks: &#8220;Summarize this renewal risk.&#8221;<\/p>\n<p>The agent decides: &#8220;Email the customer with a discount offer.&#8221;<\/p>\n<p>Helpful? Maybe. Allowed? Not unless the user asked for it.<\/p>\n<p>Store a task contract: <\/p>\n<div>\n<pre><code><span>{<\/span><span> <\/span><span>\"task_id\"<\/span><span>:<\/span><span> <\/span><span>\"task_789\"<\/span><span>,<\/span><span> <\/span><span>\"intent\"<\/span><span>:<\/span><span> <\/span><span>\"summarize_renewal_risk\"<\/span><span>,<\/span><span> <\/span><span>\"allowed_outcomes\"<\/span><span>:<\/span><span> <\/span><span>[<\/span><span>\"summary\"<\/span><span>,<\/span><span> <\/span><span>\"risk_score\"<\/span><span>,<\/span><span> <\/span><span>\"recommended_next_steps\"<\/span><span>],<\/span><span> <\/span><span>\"forbidden_outcomes\"<\/span><span>:<\/span><span> <\/span><span>[<\/span><span>\"send_email\"<\/span><span>,<\/span><span> <\/span><span>\"change_price\"<\/span><span>,<\/span><span> <\/span><span>\"update_contract\"<\/span><span>],<\/span><span> <\/span><span>\"requires_approval_for\"<\/span><span>:<\/span><span> <\/span><span>[<\/span><span>\"external_message\"<\/span><span>,<\/span><span> <\/span><span>\"billing_change\"<\/span><span>]<\/span><span> <\/span><span>}<\/span><span> <\/span><\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>Block actions that exceed the contract. This keeps agents useful without letting them expand their own authority.<\/p>\n<h2> <a name=\"control-spend-at-runtime\" href=\"#control-spend-at-runtime\"> <\/a> Control spend at runtime <\/h2>\n<p>Agent cost is not only tokens. It is scraping, enrichment, browser sessions, vector searches, retries, and external APIs.<\/p>\n<p>Track budgets per task, tenant, user, agent type, and integration. <\/p>\n<div>\n<pre><code><span>async<\/span> <span>function<\/span> <span>checkBudget<\/span><span>(<\/span><span>action<\/span><span>:<\/span> <span>AgentActionEnvelope<\/span><span>)<\/span> <span>{<\/span> <span>const<\/span> <span>spent<\/span> <span>=<\/span> <span>await<\/span> <span>getTaskSpend<\/span><span>(<\/span><span>action<\/span><span>.<\/span><span>task_id<\/span><span>);<\/span> <span>const<\/span> <span>limit<\/span> <span>=<\/span> <span>await<\/span> <span>getTaskBudget<\/span><span>(<\/span><span>action<\/span><span>.<\/span><span>task_id<\/span><span>);<\/span> <span>const<\/span> <span>estimate<\/span> <span>=<\/span> <span>action<\/span><span>.<\/span><span>estimated_cost_cents<\/span> <span>??<\/span> <span>0<\/span><span>;<\/span> <span>if <\/span><span>(<\/span><span>spent<\/span> <span>+<\/span> <span>estimate<\/span> <span>&gt;<\/span> <span>limit<\/span><span>)<\/span> <span>{<\/span> <span>return<\/span> <span>{<\/span> <span>decision<\/span><span>:<\/span> <span>\"<\/span><span>hold<\/span><span>\"<\/span><span>,<\/span> <span>reason<\/span><span>:<\/span> <span>\"<\/span><span>Task budget would be exceeded.<\/span><span>\"<\/span> <span>};<\/span> <span>}<\/span> <span>return<\/span> <span>{<\/span> <span>decision<\/span><span>:<\/span> <span>\"<\/span><span>allow<\/span><span>\"<\/span> <span>};<\/span> <span>}<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>Cost policy prevents an agent from burning money while looking productive.<\/p>\n<h2> <a name=\"use-approval-gates-without-killing-ux\" href=\"#use-approval-gates-without-killing-ux\"> <\/a> Use approval gates without killing UX <\/h2>\n<p>Bad approval UX says:<\/p>\n<blockquote>\n<p>&#8220;Approve tool call <code>send_email<\/code> with this JSON?&#8221;<\/p>\n<\/blockquote>\n<p>Better approval UX says:<\/p>\n<blockquote>\n<p>&#8220;Approve sending this renewal-risk summary to Priya at Acme Corp? No pricing changes, no attachments, no contract edits.&#8221;<\/p>\n<\/blockquote>\n<p>Approval screens should include:<\/p>\n<ul>\n<li>Plain-language summary<\/li>\n<li>Exact side effect<\/li>\n<li>Target customer or system<\/li>\n<li>Risk flags<\/li>\n<li>Diff or preview<\/li>\n<li>Cost estimate<\/li>\n<li>Approve, reject, edit, or escalate<\/li>\n<\/ul>\n<p>Store approvals as first-class records, not as buried chat history.<\/p>\n<h2> <a name=\"log-policy-decisions-like-security-events\" href=\"#log-policy-decisions-like-security-events\"> <\/a> Log policy decisions like security events <\/h2>\n<p>Log every decision, not only blocked actions.<\/p>\n<p>Include:<\/p>\n<ul>\n<li>action ID<\/li>\n<li>decision and reason<\/li>\n<li>policy IDs<\/li>\n<li>tenant, user, and agent<\/li>\n<li>tool and operation<\/li>\n<li>target resource<\/li>\n<li>argument hash<\/li>\n<li>risk flags<\/li>\n<li>cost estimate<\/li>\n<li>approval ID<\/li>\n<li>timestamp<\/li>\n<\/ul>\n<p>Avoid storing raw sensitive payloads forever. Prefer hashes, redacted previews, and short-retention encrypted payloads.<\/p>\n<h2> <a name=\"policies-to-add-first\" href=\"#policies-to-add-first\"> <\/a> Policies to add first <\/h2>\n<p>Start small:<\/p>\n<ol>\n<li>Deny production destructive actions.<\/li>\n<li>Hold external sends for approval.<\/li>\n<li>Check every resource against the active tenant.<\/li>\n<li>Block secret and PII egress.<\/li>\n<li>Hold actions above cost thresholds.<\/li>\n<li>Stop retry loops.<\/li>\n<li>Deny environment mismatch.<\/li>\n<li>Enforce autonomy mode: draft, copilot, or autopilot.<\/li>\n<\/ol>\n<p>These eight rules catch many real failures.<\/p>\n<h2> <a name=\"runtime-policy-for-mcpstyle-tools\" href=\"#runtime-policy-for-mcpstyle-tools\"> <\/a> Runtime policy for MCP-style tools <\/h2>\n<p>MCP-style registries make it easier to expose many actions. That makes policy more important.<\/p>\n<p>Keep server-owned metadata for every tool: <\/p>\n<div>\n<pre><code><span>{<\/span><span> <\/span><span>\"tool_name\"<\/span><span>:<\/span><span> <\/span><span>\"crm.update_contact\"<\/span><span>,<\/span><span> <\/span><span>\"risk_tier\"<\/span><span>:<\/span><span> <\/span><span>3<\/span><span>,<\/span><span> <\/span><span>\"operations\"<\/span><span>:<\/span><span> <\/span><span>[<\/span><span>\"write\"<\/span><span>],<\/span><span> <\/span><span>\"requires_tenant_scope\"<\/span><span>:<\/span><span> <\/span><span>true<\/span><span>,<\/span><span> <\/span><span>\"requires_approval\"<\/span><span>:<\/span><span> <\/span><span>false<\/span><span>,<\/span><span> <\/span><span>\"allowed_environments\"<\/span><span>:<\/span><span> <\/span><span>[<\/span><span>\"staging\"<\/span><span>,<\/span><span> <\/span><span>\"production\"<\/span><span>],<\/span><span> <\/span><span>\"max_calls_per_task\"<\/span><span>:<\/span><span> <\/span><span>10<\/span><span>,<\/span><span> <\/span><span>\"data_classes\"<\/span><span>:<\/span><span> <\/span><span>[<\/span><span>\"customer_profile\"<\/span><span>,<\/span><span> <\/span><span>\"email\"<\/span><span>]<\/span><span> <\/span><span>}<\/span><span> <\/span><\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>Do not trust model-facing tool descriptions as the source of truth. They are documentation, not policy.<\/p>\n<h2> <a name=\"a-practical-rollout-plan\" href=\"#a-practical-rollout-plan\"> <\/a> A practical rollout plan <\/h2>\n<ol>\n<li>Put all tools behind one executor.<\/li>\n<li>Wrap every action in an envelope.<\/li>\n<li>Log allow, deny, hold, and modify decisions.<\/li>\n<li>Add deny rules for obvious risks.<\/li>\n<li>Add approval gates for external and privileged actions.<\/li>\n<li>Add spend limits and retry breakers.<\/li>\n<li>Add policy tests.<\/li>\n<\/ol>\n<p>Example test: <\/p>\n<div>\n<pre><code><span>test<\/span><span>(<\/span><span>\"<\/span><span>agent cannot delete production customer<\/span><span>\"<\/span><span>,<\/span> <span>()<\/span> <span>=&gt;<\/span> <span>{<\/span> <span>const<\/span> <span>decision<\/span> <span>=<\/span> <span>evaluatePolicy<\/span><span>({<\/span> <span>action_id<\/span><span>:<\/span> <span>\"<\/span><span>act_test<\/span><span>\"<\/span><span>,<\/span> <span>tenant_id<\/span><span>:<\/span> <span>\"<\/span><span>t1<\/span><span>\"<\/span><span>,<\/span> <span>user_id<\/span><span>:<\/span> <span>\"<\/span><span>u1<\/span><span>\"<\/span><span>,<\/span> <span>agent_id<\/span><span>:<\/span> <span>\"<\/span><span>support_agent<\/span><span>\"<\/span><span>,<\/span> <span>session_id<\/span><span>:<\/span> <span>\"<\/span><span>s1<\/span><span>\"<\/span><span>,<\/span> <span>task_id<\/span><span>:<\/span> <span>\"<\/span><span>task1<\/span><span>\"<\/span><span>,<\/span> <span>tool_name<\/span><span>:<\/span> <span>\"<\/span><span>customer.delete<\/span><span>\"<\/span><span>,<\/span> <span>tool_version<\/span><span>:<\/span> <span>\"<\/span><span>1<\/span><span>\"<\/span><span>,<\/span> <span>operation<\/span><span>:<\/span> <span>\"<\/span><span>delete<\/span><span>\"<\/span><span>,<\/span> <span>target_resource<\/span><span>:<\/span> <span>\"<\/span><span>customer:cus_123<\/span><span>\"<\/span><span>,<\/span> <span>arguments<\/span><span>:<\/span> <span>{<\/span> <span>customer_id<\/span><span>:<\/span> <span>\"<\/span><span>cus_123<\/span><span>\"<\/span> <span>},<\/span> <span>user_intent<\/span><span>:<\/span> <span>\"<\/span><span>clean duplicate records<\/span><span>\"<\/span><span>,<\/span> <span>autonomy_mode<\/span><span>:<\/span> <span>\"<\/span><span>autopilot<\/span><span>\"<\/span><span>,<\/span> <span>environment<\/span><span>:<\/span> <span>\"<\/span><span>production<\/span><span>\"<\/span><span>,<\/span> <span>created_at<\/span><span>:<\/span> <span>new<\/span> <span>Date<\/span><span>().<\/span><span>toISOString<\/span><span>()<\/span> <span>});<\/span> <span>expect<\/span><span>(<\/span><span>decision<\/span><span>.<\/span><span>decision<\/span><span>).<\/span><span>toBe<\/span><span>(<\/span><span>\"<\/span><span>deny<\/span><span>\"<\/span><span>);<\/span> <span>});<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>Policy tests are cheap insurance.<\/p>\n<h2> <a name=\"realworld-use-cases\" href=\"#realworld-use-cases\"> <\/a> Real-world use cases <\/h2>\n<p><strong>Customer support agent:<\/strong> read tickets, draft replies, add internal notes. Hold refunds, legal statements, and external sends.<\/p>\n<p><strong>Sales research agent:<\/strong> enrich leads and draft outreach. Cap cost per lead and hold sends unless delegated.<\/p>\n<p><strong>Coding agent:<\/strong> edit files and run tests in a sandbox. Deny production database access, force pushes, secret writes, and risky shell commands.<\/p>\n<p><strong>Finance workflow agent:<\/strong> categorize invoices and draft recommendations. Require strong approval for payments, bank detail changes, and vendor notices.<\/p>\n<h2> <a name=\"mistakes-to-avoid\" href=\"#mistakes-to-avoid\"> <\/a> Mistakes to avoid <\/h2>\n<ul>\n<li>Letting the agent choose its own permissions.<\/li>\n<li>Treating tool access as binary.<\/li>\n<li>Logging only failures.<\/li>\n<li>Using approval as a substitute for deterministic policy.<\/li>\n<li>Forgetting that runaway cost is also a production failure.<\/li>\n<\/ul>\n<h2> <a name=\"final-checklist\" href=\"#final-checklist\"> <\/a> Final checklist <\/h2>\n<p>Before an agent executes a tool call, ask:<\/p>\n<ul>\n<li>Is the action wrapped in an envelope?<\/li>\n<li>Is the tenant correct?<\/li>\n<li>Is the user allowed to delegate this?<\/li>\n<li>Is the agent allowed to use this tool?<\/li>\n<li>Is the operation allowed in this autonomy mode?<\/li>\n<li>Are the arguments valid and scoped?<\/li>\n<li>Does the action match the user intent?<\/li>\n<li>Is the target environment correct?<\/li>\n<li>Does it exceed cost limits?<\/li>\n<li>Does it contain secrets or sensitive data?<\/li>\n<li>Does it require approval?<\/li>\n<li>Will the decision be logged?<\/li>\n<\/ul>\n<p>If the answer is unclear, hold or deny the action.<\/p>\n<h2> <a name=\"faq\" href=\"#faq\"> <\/a> FAQ <\/h2>\n<h3> <a name=\"what-is-ai-agent-runtime-policy\" href=\"#what-is-ai-agent-runtime-policy\"> <\/a> What is AI agent runtime policy? <\/h3>\n<p>AI agent runtime policy is a deterministic control layer that evaluates proposed agent actions before they execute. It checks permissions, scope, arguments, risk, cost, approvals, and audit requirements.<\/p>\n<h3> <a name=\"is-runtime-policy-the-same-as-prompt-guardrails\" href=\"#is-runtime-policy-the-same-as-prompt-guardrails\"> <\/a> Is runtime policy the same as prompt guardrails? <\/h3>\n<p>No. Prompt guardrails guide the model. Runtime policy enforces what the system allows. Use both, but rely on runtime policy to block dangerous tool calls.<\/p>\n<h3> <a name=\"do-small-ai-products-need-runtime-policy\" href=\"#do-small-ai-products-need-runtime-policy\"> <\/a> Do small AI products need runtime policy? <\/h3>\n<p>Yes, if agents can touch customer data, production systems, external messages, billing, or paid APIs. Start with deny rules, approval gates, tenant checks, and cost limits.<\/p>\n<h3> <a name=\"how-is-runtime-policy-different-from-normal-authorization\" href=\"#how-is-runtime-policy-different-from-normal-authorization\"> <\/a> How is runtime policy different from normal authorization? <\/h3>\n<p>Normal authorization checks what a user can do. Runtime policy also checks what the agent was delegated to do, whether the action matches intent, which autonomy mode is active, and whether the cost and risk are acceptable.<\/p>\n<h3> <a name=\"should-every-agent-action-require-approval\" href=\"#should-every-agent-action-require-approval\"> <\/a> Should every agent action require approval? <\/h3>\n<p>No. Safe reads can usually run with logging. External sends, destructive changes, billing updates, permission changes, and production operations need stronger controls.<\/p>\n<h3> <a name=\"can-runtime-policy-stop-prompt-injection\" href=\"#can-runtime-policy-stop-prompt-injection\"> <\/a> Can runtime policy stop prompt injection? <\/h3>\n<p>It cannot remove all prompt-injection risk, but it can stop injected instructions from becoming unsafe actions. Even if the model proposes a bad tool call, the policy layer can deny or hold it.<\/p>\n<h2> <a name=\"closing-thought\" href=\"#closing-thought\"> <\/a> Closing thought <\/h2>\n<p>The next step for AI products is not giving agents every tool and hoping the prompt is good enough.<\/p>\n<p>The next step is controlled execution.<\/p>\n<p>Let the model propose. Let the policy decide. Let the logs prove what happened.<\/p>\n<\/p><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Fuente: <a href=\"https:\/\/dev.to\/jackm-singularity\/ai-agent-runtime-policy-stop-dangerous-tool-calls-before-they-execute-4hp4\">Art\u00edculo original<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>A normal permission check near the API endpoint is still required, but it does not answer everything: Should the agent attempt this action at all? Does the action match the user&#8217;s request? Is the target tenant correct? Is the cost acceptable? Does it require approval? Is the agent stuck in a retry loop? Runtime policy [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":2648,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[41],"tags":[],"class_list":["post-3506","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devto"],"jetpack_publicize_connections":[],"_links":{"self":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts\/3506","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/comments?post=3506"}],"version-history":[{"count":0,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts\/3506\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/media\/2648"}],"wp:attachment":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/media?parent=3506"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/categories?post=3506"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/tags?post=3506"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}