Actions and escalation
result = st.guard("process_refund", {"amount": 4200, "user_id": "u_42"})
match result.action:
case "allow":
run(tool_input=result.modified_input or original_input)
case "modify":
run(tool_input=result.modified_input)
case "block":
log(result.reason)
case "escalate":
queue_for_human(result.escalation_id)allow
Proceed as requested. Common path. No extra work.
block
Do not execute the tool.
- In
patch_anthropic/patch_openai:staso.GuardBlockedis raised. Catch and recover. - In
st.guard(...): return early, logresult.reason, logresult.rules_triggered.
modify
Guard rewrote the input. Use result.modified_input.
result = st.guard("send_email", {"to": "[email protected]", "body": "..."})
if result.action == "modify":
send_email(**result.modified_input)In patched integrations, the rewrite is applied to the provider's tool-call object automatically — your agent loop sees the safer input transparently.
escalate
A human must approve before the call runs.
Fire-and-forget. Return immediately with result.escalation_id. Surface it in your app. Treat escalate like block for now and don't run the tool.
Synchronous wait. Pass wait_for_escalation=True and Guard polls until the escalation resolves, returning allow (approved) or block (denied / timed out).
result = st.guard(
"process_refund",
{"amount": 10000},
wait_for_escalation=True,
escalation_timeout=300.0, # max 5 minutes
escalation_poll_interval=3.0, # check every 3s
)
if result.action == "allow":
run_refund(amount=10000)
else:
log(f"escalation denied: {result.reason}")Defaults: escalation_poll_interval=3.0, escalation_timeout=300.0. Tighten for chat UX, loosen for batch jobs.
Trace visibility
Every decision becomes a child span: guard:blocked:<tool>, guard:modified:<tool>, or guard:would-block:<tool> (audit-mode rules). Open any trace to see what Guard did and why.
Next
- Manual checks — full
st.guard(...)signature. - Rules and policies