Wrap unsafe agent actions with one decorator. Shadow-mode by default.
Project description
supervisor-guards
Wrap unsafe agent actions with one decorator. Shadow-mode by default — logs would-have-blocked without interrupting production traffic. Flip to enforce only when you've seen enough shadow data to trust the policy.
Works with any Python 3.10+ app. For the TypeScript equivalent, see @runtime-supervisor/guards (same semantics, same env vars).
Install
pip install supervisor-guards
Configure once
from supervisor_guards import configure
configure(
base_url="https://vibefixing.ngrok.app",
app_id=os.environ["SUPERVISOR_APP_ID"], # from POST /v1/integrations
shared_secret=os.environ["SUPERVISOR_SECRET"],
enforcement_mode="shadow", # "shadow" (default) | "sample" | "enforce"
)
All config reads env vars as fallback, so the call above can be zero-arg if you set SUPERVISOR_* in your environment. SUPERVISOR_ENFORCEMENT_MODE flips the mode without a code change.
Wrap an action
Two forms — pick whichever reads better for the call site.
Decorator — sync or async:
from supervisor_guards import supervised, supervised_async
@supervised_async(
"refund",
payload=lambda order_id, amount: {"order_id": order_id, "amount": amount, "currency": "USD"},
)
async def do_refund(order_id: str, amount: int) -> dict:
return await stripe.refunds.create_async(payment_intent=order_id, amount=amount)
# Use normally — the supervisor check happens before stripe is called.
await do_refund("pi_abc", 4200)
For synchronous functions, use @supervised(...) with the same payload= kwarg. If you omit payload=, the decorator captures (args, kwargs) as the payload automatically.
Imperative (easier to bolt onto existing code):
from supervisor_guards import guarded
def send_receipt_email(user, order):
return guarded(
"email_send",
{"to": user.email, "subject": "Your receipt"},
mailer.send,
to=user.email,
subject="Your receipt",
html=render_receipt(order),
)
guarded(action_type, payload, fn, *args, **kwargs) pre-checks then calls fn(*args, **kwargs). For async, wrap with asyncio.to_thread or use the supervised_async decorator.
If the supervisor would deny, guarded raises SupervisorBlocked. In shadow mode (default) the call always runs and a would-have-blocked is logged + sent to your dashboard.
Modes (via SUPERVISOR_ENFORCEMENT_MODE)
| Mode | Behavior |
|---|---|
shadow |
Always allow; log every would-be-block. Default. Safe to deploy day-1. |
sample |
Enforce for SUPERVISOR_SAMPLE_PERCENT of traffic (hash-stable), shadow the rest. |
enforce |
Block denies, wait for review approval on escalations. |
On-review behavior
When the supervisor escalates an action to human review, you choose what the wrapped function does:
configure(default_on_review="block")
block(default) — polls until reviewer approves/rejects, up toreview_timeout_ms. Timeout counts as deny.fail_closed— raisesSupervisorReviewPendingimmediately. Use when you can't hold the call.fail_open— proceeds as if allowed. Use only when the action is fully reversible.shadow— logs and proceeds. For passive observation.
Override per call site via on_review= kwarg.
Errors
from supervisor_guards import SupervisorBlocked, SupervisorReviewPending
try:
await do_refund("pi_abc", 99999)
except SupervisorBlocked as e:
# policy denied; e.reasons explains why, e.action_id is the audit reference
log.warn("blocked by supervisor", reasons=e.reasons)
except SupervisorReviewPending as e:
# fail_closed mode; action is queued for review
enqueue_manual_review(e.action_id)
What you see in the dashboard
Every guarded call — shadow, sample, or enforce — shows up at vibefixing.me/dashboard: live decisions, would-block rate, pending reviews, latency p95. Shadow mode is how you build confidence before flipping enforce.
License
Apache-2.0. Copyright 2026 Ariel San Martín.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file supervisor_guards-0.3.0.tar.gz.
File metadata
- Download URL: supervisor_guards-0.3.0.tar.gz
- Upload date:
- Size: 16.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cdc5601642021188b9fb561c8b90cc8ae0240c83eeaaee0ba58db8d3dbfaea3b
|
|
| MD5 |
a7ee5d41afe7775c29a31c1a89382319
|
|
| BLAKE2b-256 |
bcbf4b4672565df1c386ad26d597f2eaf3cd4015a486fa0fb3c01c8dbe8c6fa2
|
File details
Details for the file supervisor_guards-0.3.0-py3-none-any.whl.
File metadata
- Download URL: supervisor_guards-0.3.0-py3-none-any.whl
- Upload date:
- Size: 14.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9cae10866bf0ad2c3c0355295cb0b2038bd2df242d4789eb5a437fb4300ce770
|
|
| MD5 |
e9e37a35fff8367e3467f3df0f27e841
|
|
| BLAKE2b-256 |
83f6c922affde4e6a63ddfcd8c39586d8133ac7961d4e9dfc9d298f7a4a07feb
|