A human-approval gate for the dangerous things an AI agent does.
Project description
airlock-hitl
A human-approval gate for the dangerous things an AI agent does. This is the Python package; it mirrors the TypeScript package one-to-one. The full story, design notes, and a live dashboard demo are in the repository.
An agent that can act on untrusted input is dangerous: a prompt injection or a
plain mistake can make it pay, email, or delete the wrong thing — and a system
prompt won't stop it. Airlock assumes the model will be hijacked and puts the
safety boundary in the architecture: every tool you mark SENSITIVE pauses
for a human to approve, edit, or reject before it runs.
Install
pip install airlock-hitl # or: uv add airlock-hitl
The distribution is named airlock-hitl; the import is airlock. Requires
Python 3.13+.
Quickstart
The agent wants to send an email — the run pauses before it happens, and the email is sent only once you approve.
import asyncio
from airlock import (
Agent,
AgentDependencies,
ApproveDecision,
CompletionResult,
FakeLlmProvider,
InMemoryAuditSink,
InMemoryEventBus,
InMemoryRunStore,
RiskBasedGatePolicy,
RiskTier,
SystemClock,
Tool,
ToolCall,
ToolCallId,
UuidIdGenerator,
)
async def send_email(args):
print("SENDING:", dict(args))
return "sent"
# Mark the tool SENSITIVE: it must be approved before it runs.
tool = Tool(
name="send_email",
description="Send an email",
parameters={"type": "object"},
risk=RiskTier.SENSITIVE,
handler=send_email,
)
# A scripted model — swap for AnthropicProvider / OpenAiProvider in production.
provider = FakeLlmProvider(
[
CompletionResult(
text=None,
tool_calls=(ToolCall(id=ToolCallId("1"), name="send_email", args={"to": "alice@example.com"}),),
),
CompletionResult(text="Done.", tool_calls=()),
]
)
agent = Agent(
AgentDependencies(
provider=provider,
tools=[tool],
events=InMemoryEventBus(),
store=InMemoryRunStore(),
audit=InMemoryAuditSink(),
clock=SystemClock(),
ids=UuidIdGenerator(),
gate_policy=RiskBasedGatePolicy(),
system_prompt="You are a support agent.",
)
)
async def main():
paused = await agent.run("Email Alice a refund confirmation")
print(paused.status) # "awaiting_approval" — nothing sent yet
done = await agent.resume(
paused.run_id, paused.approval.request_id, ApproveDecision(approver="you@example.com")
)
print(done.status) # "completed" — now the email was sent
asyncio.run(main())
In production, swap FakeLlmProvider for AnthropicProvider or OpenAiProvider,
and the in-memory adapters for RedisRunStore + RedisEventBus so runs pause and
resume across processes. Runnable end-to-end demos (CLI approver, Redis, the
dashboard) are in the
repository.
What you get
- An
Agentthat runs a tool-use loop and pauses before any tool you markRiskTier.SENSITIVE, until a human approves, edits, or rejects it. - Model-agnostic providers (
AnthropicProvider,OpenAiProvider) over an injectedhttpxclient — no vendor SDKs. - A Redis store and Pub/Sub bus for pause/resume across processes, plus in-memory fakes for tests and demos.
Layout (hexagonal — imports point inward)
airlock.domain— tools, risk tiers, messages, runs, events, errors. No I/O.airlock.application— the agent loop, the gate policy, and the ports (Protocols).airlock.infrastructure— adapters: providers, the Redis store and bus, audit sinks, clocks, id generators, and the in-memory fakes.airlock.interface— driving adapters: the runner and the approver.airlock.core—Settings(pydantic-settings).
License
MIT — see the repository for contributing and the full engineering write-up.
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 airlock_hitl-0.1.1.tar.gz.
File metadata
- Download URL: airlock_hitl-0.1.1.tar.gz
- Upload date:
- Size: 58.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
463bc80cd2057689110bff7072297ddd9d8e1f2f804e519373836fdbe92269eb
|
|
| MD5 |
b1f129929680753be45255393b7de2cc
|
|
| BLAKE2b-256 |
6282aac7d1af6ad539f3ac7513b70b3dc4d9ec8c39b07ab8044bc06b12764538
|
Provenance
The following attestation bundles were made for airlock_hitl-0.1.1.tar.gz:
Publisher:
release.yml on fedorello/airlock
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
airlock_hitl-0.1.1.tar.gz -
Subject digest:
463bc80cd2057689110bff7072297ddd9d8e1f2f804e519373836fdbe92269eb - Sigstore transparency entry: 1953386097
- Sigstore integration time:
-
Permalink:
fedorello/airlock@ece7c133b5055d95df6c90c54057d8ec01d779db -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/fedorello
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ece7c133b5055d95df6c90c54057d8ec01d779db -
Trigger Event:
push
-
Statement type:
File details
Details for the file airlock_hitl-0.1.1-py3-none-any.whl.
File metadata
- Download URL: airlock_hitl-0.1.1-py3-none-any.whl
- Upload date:
- Size: 29.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9ce234b8ed7ea817df8e6040fa4704840131e39df6ce1acb6a76e71aba20d42b
|
|
| MD5 |
04fd855b769a11183bd8bf97db56951f
|
|
| BLAKE2b-256 |
407ba83ea3315922ea1d3c2deb15cc4b59a6ae9643ae6175749ce981db24bbcb
|
Provenance
The following attestation bundles were made for airlock_hitl-0.1.1-py3-none-any.whl:
Publisher:
release.yml on fedorello/airlock
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
airlock_hitl-0.1.1-py3-none-any.whl -
Subject digest:
9ce234b8ed7ea817df8e6040fa4704840131e39df6ce1acb6a76e71aba20d42b - Sigstore transparency entry: 1953386585
- Sigstore integration time:
-
Permalink:
fedorello/airlock@ece7c133b5055d95df6c90c54057d8ec01d779db -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/fedorello
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ece7c133b5055d95df6c90c54057d8ec01d779db -
Trigger Event:
push
-
Statement type: