Rules and Actions
Guard rules are configured in your Staso dashboard. The SDK sends tool calls to the guard API, which evaluates them against your rules and returns a decision.
Rule Evaluation Flow
Your agent calls guard(tool_name, tool_input)
|
v
Staso evaluates all matching rules
|
v
Each rule returns: passed/failed, action, reason, score
|
v
Final decision: the strictest action wins
|
v
GuardResult returned to your codeMultiple rules can match a single tool call. The strictest action wins -- if one rule says "allow" and another says "block", the result is "block".
Actions
allow
The tool call passes all rules. Execute normally.
result = guard(tool_name="search_kb", tool_input={"query": "pricing"})
# result.action == "allow"
# result.reason == ""block
The tool call violates a rule. Do not execute.
result = guard(tool_name="drop_table", tool_input={"table": "users"})
# result.action == "block"
# result.reason == "Destructive database operations are not allowed"
# result.severity == "high"
# result.rule_name == "no_destructive_db_ops"modify
The tool call is allowed, but the input needs adjustment. The guard returns sanitized input.
result = guard(tool_name="send_email", tool_input={"body": "SSN: 123-45-6789"})
# result.action == "modify"
# result.modified_input == {"body": "SSN: ***-**-****"}
# result.modifications == [{"field": "body", "reason": "PII redacted"}]Always use result.modified_input when the action is modify:
if result.action == "modify":
execute_tool(**result.modified_input)escalate
The tool call needs human approval before proceeding.
result = guard(tool_name="wire_transfer", tool_input={"amount": 50000})
# result.action == "escalate"
# result.escalation_id == "esc_abc123"See Escalation for the full workflow.
Severity Levels
Every guard result includes a severity:
| Severity | Meaning |
|---|---|
low | Informational. Rule triggered but impact is minimal. |
medium | Notable. Review recommended. |
high | Critical. Action was blocked or requires immediate attention. |
if result.severity == "high":
alert_team(result.reason)GuardResult
The complete result object returned by guard():
from staso import GuardResult
result = guard(tool_name="...", tool_input={...})| Field | Type | Description |
|---|---|---|
action | str | "allow", "block", "modify", or "escalate" |
reason | str | Human-readable explanation of the decision |
rule_name | str | None | Name of the primary rule that triggered |
severity | str | "low", "medium", or "high" |
results | list[GuardRuleResult] | Individual results from each evaluated rule |
modified_input | dict | None | Sanitized tool input (only when action is "modify") |
modifications | list[dict] | List of modifications applied to the input |
escalation_id | str | None | Escalation identifier (only when action is "escalate") |
latency_ms | float | Time taken for guard evaluation in milliseconds |
rules_triggered | list[str] | Names of all rules that fired |
GuardRuleResult
Each rule that evaluated the tool call returns an individual result:
for rule in result.results:
print(f"{rule.rule_name}: {'passed' if rule.passed else 'failed'} ({rule.action})")| Field | Type | Description |
|---|---|---|
rule_id | str | Unique rule identifier |
rule_name | str | Human-readable rule name |
passed | bool | Whether the tool call passed this rule |
score | float | None | Confidence score (0.0 to 1.0) if applicable |
action | str | Action this rule recommends: "allow", "block", "audit", "modify", "escalate" |
reason | str | Why this rule triggered |
latency_ms | float | Evaluation time for this specific rule |
violations | list[dict] | Detailed violation data from this rule |
Audit Rules
Rules with action "audit" evaluate and record results but don't affect the final decision. Use audit rules to test new rules in production before enforcing them.
# An "allow" result can still have audit violations
if result.action == "allow" and result.results:
audit_failures = [r for r in result.results if not r.passed]
if audit_failures:
log.info(f"Audit: {len(audit_failures)} rules would have triggered")In CLI agent integrations (Claude Code, Codex), audit violations create guard:would-block spans on your dashboard.
Timeout and Error Handling
result = guard(
tool_name="my_tool",
tool_input={"key": "value"},
timeout=5.0, # seconds, default is 10.0
)If the guard API is unreachable or times out, the result is:
# result.action == "allow"
# result.reason == "Guard check failed: ..."Guards fail open. A guard outage never blocks your agent from operating. Failures are logged as warnings.
Next
- Escalation -- human-in-the-loop approval workflows
- Guard integration -- add guards to your agent
- Configuration -- environment variables for guard behavior