Skip to content

Approval Handlers

Approval handlers control how humans are asked to approve or deny actions.

Built-in Handlers

CLIApprovalHandler (default)

Prompts interactively in the terminal:

============================================================
  APPROVAL REQUIRED
============================================================
  Action:  bulk_update
  Target:  salesforce
  Risk:    HIGH
  Params:  {'count': 150}
============================================================
  Approve? [y/n]:

AutoApprovalHandler

Approves everything automatically. For testing only.

from aegis.runtime.approval import AutoApprovalHandler

runtime = Runtime(
    executor=my_executor,
    policy=my_policy,
    approval_handler=AutoApprovalHandler(),
)

CallbackApprovalHandler

Use a sync or async callback function for programmatic approval:

from aegis.runtime.approval_callback import CallbackApprovalHandler

# Sync callback
handler = CallbackApprovalHandler(
    callback=lambda decision: decision.risk_level.value <= 2  # Auto-approve low/medium
)

# Async callback
async def check_approval(decision):
    # Query your approval system
    return await my_approval_api.check(decision.action.type)

handler = CallbackApprovalHandler(callback=check_approval)

runtime = Runtime(
    executor=my_executor,
    policy=my_policy,
    approval_handler=handler,
)

Custom Handlers

Implement the ApprovalHandler interface:

from aegis.runtime.approval import ApprovalHandler
from aegis.core.policy import PolicyDecision

class SlackApprovalHandler(ApprovalHandler):
    def __init__(self, channel: str, bot_token: str):
        self._channel = channel
        self._token = bot_token

    async def request_approval(self, decision: PolicyDecision) -> bool:
        # Post to Slack and wait for reaction
        message = (
            f"*Approval Required*\n"
            f"Action: `{decision.action.type}` on `{decision.action.target}`\n"
            f"Risk: {decision.risk_level.name}\n"
            f"React with :white_check_mark: to approve or :x: to deny"
        )
        # ... send message, wait for reaction ...
        return approved
runtime = Runtime(
    executor=my_executor,
    policy=my_policy,
    approval_handler=SlackApprovalHandler(
        channel="#agent-approvals",
        bot_token="xoxb-...",
    ),
)

Handler Contract

Your handler must implement one async method:

async def request_approval(self, decision: PolicyDecision) -> bool
  • Receives the full PolicyDecision (action, risk level, matched rule)
  • Returns True to approve, False to deny
  • Can be async — take as long as needed (wait for human input)
  • Should display enough context for an informed decision