Execution safety for AI agent tool calls: idempotency, semantic caching, singleflight, policy, budgets, a kill switch, approval gating, and a reasoning-linked audit log, as a library, not a service.
Project description
tbay
Execution safety for AI agent tool calls: idempotency, TTL and semantic caching, singleflight deduplication, risk-tiered policy, spend budgets, a cross-process kill switch, human approval gating (optionally cryptographically signed), crash recovery, lifecycle events with optional OpenTelemetry spans, and a reasoning- and agent-linked audit log. A library you install, not a service you depend on.
from tbay import TbayClient, guarded
client = TbayClient("postgresql://postgres:tbay@localhost:5432/tbay")
# also: "sqlite:///~/.tbay/db.sqlite" or "redis://localhost:6379/0"
@guarded(client, policy="readonly")
def github_search(query: str) -> dict:
return real_github_api_call(query)
@guarded(client, policy="destructive")
def refund_customer(customer_id: str, amount: float) -> dict:
return stripe_refund(customer_id, amount)
github_search now caches and collapses concurrent duplicates;
refund_customer never double-runs and pauses for a human before
executing. @guarded only wraps a plain callable, so it drops in under
LangChain's @tool, the OpenAI Agents SDK's @function_tool, CrewAI
tools, an MCP server's tool handlers, or bare functions
(recipes).
When something goes wrong anyway, you have an emergency brake and a ceiling on the damage:
# policy.yaml: refunds may total at most $1000/day, across every process
policies:
destructive:
approval_required: true
budget: {arg: amount, max: 1000, per: 1d}
$ tbay pause --reason "agent runaway" # stop every guarded call, everywhere, now
$ tbay resume
Agent frameworks solve planning and orchestration; none of them solve execution safety. Once a tool is selected, nothing stops it from being called twice, called too often, or fired on a destructive action without a human in the loop. tbay sits underneath any framework and handles that, durably, across processes, in whatever database you already run. Not a hosted service: state lives entirely in a database you own; nothing calls home.
Install
pip install tbay # SQLite backend, stdlib only
pip install tbay[postgres] # + Postgres
pip install tbay[redis] # + Redis
# or: uv add tbay / "tbay[postgres]" / "tbay[redis]"
Documentation
| Guide | What it covers |
|---|---|
| Quickstart | First guarded tool, the dev container, running the demo |
| Tutorial | 13 runnable, narrated scripts: every feature, step by step, SQLite-only |
| Design rationale | Why each mechanism works the way it does |
| Policies | The four risk tiers, the YAML file, every policy field |
| Caching and idempotency | Idempotency keys, TTL, singleflight, semantic caching, volatile calls, rate limits |
| Approvals | Pause/approve flow, signed webhooks, bypass thresholds, signed approvals, rejection reasons |
| Runtime controls | The kill switch (tbay pause), spend budgets, stale-lease crash recovery |
| Observability | Audit log, lifecycle events, OpenTelemetry, reasoning traces, agent identity, the CLI, the dashboard |
| Integrations | LangChain, OpenAI Agents SDK, CrewAI, MCP servers, guard_tools |
| Storage backends | SQLite, Postgres, Redis: guarantees, schema, migrations, clearing data |
| API reference | Every public function and class, with examples |
Try it in two minutes
Open the repo in VS Code, run "Dev Containers: Reopen in Container" (bundles Python + Postgres + Redis, nothing to install), then:
uv run python dashboard/app.py # terminal 1: live dashboard on port 8787
uv run python examples/demo.py # terminal 2: every feature, one run
The demo ends blocked on a $500 refund; approve or reject it from the dashboard and watch the blocked call resume or learn why it was refused. Details in the Quickstart.
Development
uv sync --extra dev
uv run pytest
Postgres- and Redis-backed tests need TBAY_TEST_PG_DSN /
TBAY_TEST_REDIS_URL; CI and the dev container provide both.
License
MIT
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 tbay-0.3.0.tar.gz.
File metadata
- Download URL: tbay-0.3.0.tar.gz
- Upload date:
- Size: 61.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f933625b87882f355e4a1be8b1c3db01391e1b720044c663d48610760a51850c
|
|
| MD5 |
894e9d3a0c404d813a6f66e208500f03
|
|
| BLAKE2b-256 |
336f89f2fc2a0404f822151aa8d64be64e3caf91d91168d8a13ccd89892fc96f
|
File details
Details for the file tbay-0.3.0-py3-none-any.whl.
File metadata
- Download URL: tbay-0.3.0-py3-none-any.whl
- Upload date:
- Size: 52.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9792ac100809c96d0b03c88fb3394a172382fee88edf39a5ac6701ce9f3eb06e
|
|
| MD5 |
2b8cbd1ad5daa08f5de0b8b1ade47597
|
|
| BLAKE2b-256 |
be8a7706cf1de745a51a95b7dc033acf4ed92e791d1f5d7ef51aa840e8e56de0
|