Staso Docs
Guards

Escalation

Some decisions are too important for rules alone. Escalation pauses the agent and waits for a human to approve or deny the action.

When Escalation Happens

A guard rule returns "escalate" when the tool call matches an escalation condition -- typically high-value transactions, irreversible operations, or edge cases where automated rules can't confidently decide.

result = guard(
    tool_name="wire_transfer",
    tool_input={"amount": 50000, "destination": "external-bank"},
)

# result.action == "escalate"
# result.escalation_id == "esc_abc123"

Configure escalation rules in your Staso dashboard. Example: "Escalate any refund over $1,000" or "Escalate database deletions in production."

Two Modes

Fire-and-Forget (Default)

Get the escalation ID back immediately. Handle the pending state in your application.

result = guard(
    tool_name="wire_transfer",
    tool_input={"amount": 50000},
)

if result.action == "escalate":
    # Notify the user, queue the action, or return a pending status
    return f"This transfer requires approval. Reference: {result.escalation_id}"

Use this when your agent runs in a request-response model (API endpoints, chatbots) where you can't block the thread.

Polling (Synchronous Wait)

Block until a human approves or denies. The SDK polls the escalation status automatically.

result = guard(
    tool_name="wire_transfer",
    tool_input={"amount": 50000},
    wait_for_escalation=True,           # enable polling
    escalation_poll_interval=3.0,       # check every 3 seconds (default)
    escalation_timeout=300.0,           # give up after 5 minutes (default)
)

# After a human responds:
# result.action == "allow"   (approved)
# result.action == "block"   (denied or timeout)

Use this for background workers, batch jobs, or agents where blocking is acceptable.

Polling Parameters

ParameterTypeDefaultDescription
wait_for_escalationboolFalseEnable synchronous polling
escalation_poll_intervalfloat3.0Seconds between status checks
escalation_timeoutfloat300.0Max seconds to wait before timing out

Timeout Behavior

If the human doesn't respond within escalation_timeout:

# result.action == "block"
# result.reason == "Escalation poll timeout"

Timeouts are treated as denials. The tool does not execute.

Resolution States

StatusResult ActionMeaning
approvedallowHuman approved -- safe to execute
deniedblockHuman denied -- do not execute
timeoutblockNo response within the timeout window

Example: Refund with Escalation

import staso as st
from staso import guard

st.init(api_key="ak_...", agent_name="refund-agent")

@st.tool(name="process_refund")
def process_refund(customer_id: str, amount: float) -> str:
    result = guard(
        tool_name="process_refund",
        tool_input={"customer_id": customer_id, "amount": amount},
        wait_for_escalation=True,
        escalation_timeout=120.0,  # 2 minutes for manager to respond
    )

    if result.action == "block":
        return f"Refund denied: {result.reason}"

    if result.action == "allow":
        do_refund(customer_id, amount)
        return f"Refund of ${amount} processed"

    # "modify" -- amount was adjusted
    adjusted = result.modified_input["amount"]
    do_refund(customer_id, adjusted)
    return f"Refund of ${adjusted} processed (adjusted from ${amount})"

st.shutdown()

Dashboard

Escalation events show up on your Staso dashboard with:

  • The tool name and input that triggered escalation
  • The rule that caused the escalation
  • The resolution (approved, denied, or timed out)
  • Time to resolution

Next