Skip to main content

๐Ÿ›ก๏ธ PreCheck Guardian

A pre-execution approval gate for AI agents. Preview the full plan, see the risk of every step, then approve, reject, or edit โ€” before anything runs.

๐Ÿงฉ Part of the Agent Loop Toolkit โ€” three small, zero-dependency, framework-agnostic libraries you bolt onto any agent loop. Each works standalone; together they cover context โ†’ gate โ†’ steer.

Where it plugs in Library What it does
The context going in context-compressor Shrink the LLM context window 40โ€“80% โ€” drop noise, redundancy, long-tail detail
The plan, before a step runs โ† you are here precheck-guardian Preview the plan, see per-step risk, approve / reject / edit
The run, while it's live something-else Interject, pause, or guard a live loop without restarting

PyPI CI python license deps

PreCheck Guardian rendering an agent plan with per-step risk levels and an approve/reject/edit prompt

Autonomous agents act fast and don't ask. One rm -rf, one DROP TABLE, one force-push, and the damage is done. PreCheck Guardian inserts a human-in-the-loop checkpoint between planning and execution: it parses what the agent is about to do, flags the dangerous steps, and waits for a human to sign off.

Framework-agnostic. No dependency on any specific agent framework. It's one function call โ€” wire it into LangChain, a custom ReAct loop, or your own tool runner. Zero required dependencies; rich/questionary are optional niceties.


Why this exists

As agents get more autonomous, the gap between "here's what I'll do" and "...and it's already done" gets dangerous. Most "human-in-the-loop" options today force a bad trade-off:

Approach Sees the whole plan first? Per-step risk scoring? Audit trail? Drop-in?
Just let the agent run โŒ โŒ โŒ โ€”
A raw input("y/n?") per tool โŒ (one step at a time) โŒ โŒ manual
Framework-specific approval callback partial โŒ โŒ locked to one framework
PreCheck Guardian โœ… โœ… (75 rules) โœ… (JSON-Lines) โœ… one call, any framework

You get the full plan up front, the risky steps highlighted, a real approve / reject / edit decision, and a compliance-ready audit log โ€” in one call, with zero required dependencies.


See it in 10 seconds

pip install precheck-guardian
python -m approval_hook        # render a sample plan with risk levels
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Approval Required โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ Demo Plan        ๐ŸŸข 1  ๐ŸŸก 1  ๐Ÿ”ด 2  โ›” 2    โฑ 15s                     โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
  #  Risk      Action                                  Tool
  1  LOW       SELECT * FROM users WHERE status=...    sql_query
  3  HIGH      UPDATE accounts SET billed = 1 ...      generic_tool   โš  modifies rows
  4  CRITICAL  DROP TABLE users_staging                sql_query      โš  destroys data
  5  CRITICAL  rm -rf /tmp/build                       file_delete    โš  unrecoverable
  6  HIGH      git push --force origin release         git            โš  overwrites history

Use it in 3 lines

from approval_hook import ApprovalGuard

guard = ApprovalGuard()
if guard.is_approved(agent_plan_text):
    run(agent_plan_text)          # only runs after a human approves

is_approved parses the plan, scores each step, prints it, prompts the operator, and writes an audit-log entry โ€” all in one call.


What it does

Capability Description
๐Ÿงฉ Plan parsing Turns free-form agent output (numbered lists, bullets, Step 1:โ€ฆ) or structured tool calls into typed steps.
๐Ÿšฆ Risk scoring 75 rule-based detectors across destruction, privilege, system control, RCE/supply-chain, infra, secrets and network. Conservative by design โ€” when unsure, it scores higher.
๐Ÿ‘ค Human approval Interactive Approve / Reject / Edit โ€” inline prompt, or a full-screen Textual TUI (optional).
๐Ÿชœ Policy gates Auto-approve LOW risk, prompt on MEDIUM+, optionally hard-block CRITICAL. Safe default: refuse, don't auto-run, when there's no human (CI).
๐Ÿ” Plan diffing Compare a revised plan against the original โ€” unified diff + a clean per-step summary.
๐Ÿ“ Audit trail Every decision appended to a JSON-Lines log (plan snapshot, max risk, actor, reason) for compliance. Secrets are auto-redacted.
๐Ÿ”’ Secret redaction Params like password, api_key, token are masked everywhere they're shown or logged.

Install

pip install precheck-guardian            # core, zero dependencies
pip install "precheck-guardian[all]"     # + rich tables, questionary menus, Textual TUI

Or from a local clone (for development):

git clone https://github.com/uninhibited-scholar/precheck-guardian
cd precheck-guardian
pip install -e ".[dev]"

How it fits into an agent loop

agent plans  โ”€โ–ถ  PreCheck Guardian  โ”€โ–ถ  approved?  โ”€โ–ถ  execute tools
                  โ”‚  parse                  โ”‚  no
                  โ”‚  score risk             โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ  abort / replan
                  โ”‚  show to human
                  โ””โ”€ record decision

Structured tool calls

from approval_hook import ApprovalGuard, ApprovalConfig

guard = ApprovalGuard(ApprovalConfig(block_critical=True, actor="ci-bot"))

decision = guard.review([
    {"tool": "read_data",  "args": {"path": "/data/in.csv"}, "description": "load input"},
    {"tool": "file_delete","args": {"path": "/data/in.csv"}, "description": "rm -rf /data/in.csv"},
])

if decision.proceed:
    execute(...)

Native OpenAI / LangChain tool-call traces

Already have a raw tool-call trace from the model? Hand it over as-is โ€” the guard detects the shape and parses it (OpenAI arguments JSON strings included):

# an OpenAI chat-completion response, an assistant message, a message list,
# or a list of tool-call dicts โ€” all work:
guard.review(openai_response)
guard.review([{"type": "function",
               "function": {"name": "run_sql", "arguments": '{"statement": "DROP TABLE t"}'}}])

Tuning the policy

from approval_hook import ApprovalConfig, RiskLevel

ApprovalConfig(
    require_above=RiskLevel.LOW,    # prompt for MEDIUM and up (None = always prompt)
    block_critical=False,           # True = auto-reject CRITICAL, never even ask
    audit_path="approval_audit.jsonl",
    actor="alice",
    non_interactive_default=None,   # what to do with no TTY; None = safe REJECT
)

One-line decorators

Add a checkpoint without restructuring code:

from approval_hook import gate_plan, review_result, guard_callable

@gate_plan("plan")               # review the plan passed in; skip body if rejected
def execute(plan): ...

@review_result()                 # review what the function returns; raise if rejected
def make_plan() -> str: ...

# wrap a single dangerous tool โ€” every call is reviewed (works with LangChain Tool(func=...))
safe_delete = guard_callable(delete_file, tool_name="delete_file")

LangChain

Wrap any LangChain tool so every call is reviewed first โ€” a drop-in replacement that keeps the tool's name, description and argument schema:

from langchain_core.tools import tool
from approval_hook.integrations.langchain import guard_langchain_tool

@tool
def delete_path(path: str) -> str:
    """Delete a file or directory."""
    ...

safe_delete = guard_langchain_tool(delete_path)   # give the agent this instead
# safe_delete.invoke({"path": "rm -rf /data"}) -> shown for approval / blocked

Install the optional extra: pip install "precheck-guardian[langchain]" (Python 3.10+).

Full-screen TUI

Prefer a richer review screen? Swap in the Textual UI โ€” same guard, same audit log:

from approval_hook import ApprovalGuard
from approval_hook.ui.tui import TextualApprovalUI

guard = ApprovalGuard(ui=TextualApprovalUI())
guard.review(agent_plan)     # opens an interactive approval screen

The PreCheck Guardian Textual TUI showing a 5-step plan with per-step risk levels and Approve/Edit/Reject buttons

Install the optional extra: pip install "precheck-guardian[tui]" (Python 3.9+).

Custom risk rules

from approval_hook import RiskAnnotator, RiskLevel
from approval_hook.core.risk_annotator import _rule

annotator = RiskAnnotator()
annotator.add_rule(_rule("no_prod", r"\bprod\b", RiskLevel.CRITICAL,
                         "Touches production.", "Use staging instead."))
guard = ApprovalGuard(annotator=annotator)

Audit log & reporting

Every decision is appended to a JSON-Lines log. Summarise it from the CLI:

precheck audit --summary --limit 5
Audit summary โ€” approval_audit.jsonl
  total decisions : 128
  approved        : 119
  rejected        : 9
  approval rate   : 93%
  by max risk     :
    ๐ŸŸข low 71   ๐ŸŸก medium 33   ๐Ÿ”ด high 18   โ›” critical 6

Or in code: from approval_hook.audit.logger import summarize_records.


Examples

python examples/basic_approval.py        # interactive approve/reject/edit
python examples/with_diff.py             # diff an edited plan vs. the original
python examples/langchain_style_hook.py  # wire into an agent loop
python examples/decorator_gate.py        # one-line decorator + per-tool gating
python examples/langchain_real.py        # gate a real LangChain tool (needs langchain-core)
python examples/agent_workflow.py         # end-to-end multi-step agent; destructive step blocked
python examples/tui_approval.py           # full-screen Textual approval UI (needs textual)

Testing

pytest          # 67 tests, <1s

Design notes & honest scope

  • Risk scoring is rule-based, not a sandbox. It's a strong heuristic safety net to surface obvious danger for a human โ€” it is not a guarantee that an unflagged step is safe. Keep the human in the loop for anything destructive.
  • The parser is best-effort. Unstructured text becomes a single step rather than being silently dropped. For exact fidelity, feed it structured tool calls.
  • Zero core dependencies on purpose โ€” easy to vendor, easy to trust.

Contributions of new risk rules, parser formats and framework adapters are welcome.

License

MIT โ€” see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

precheck_guardian-0.1.1.tar.gz (39.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

precheck_guardian-0.1.1-py3-none-any.whl (35.7 kB view details)

Uploaded Python 3

File details

Details for the file precheck_guardian-0.1.1.tar.gz.

File metadata

  • Download URL: precheck_guardian-0.1.1.tar.gz
  • Upload date:
  • Size: 39.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for precheck_guardian-0.1.1.tar.gz
Algorithm Hash digest
SHA256 f965531cab03550169c97e42a7fee9720686dab7fcbabbe76e2b6e9cba0d6eeb
MD5 93db5732bd10318e1a5aca7340a0c509
BLAKE2b-256 baee62c12e88cb36f737b59d8128bbfb4597b62abca2ebb7570468608e525a1f

See more details on using hashes here.

Provenance

The following attestation bundles were made for precheck_guardian-0.1.1.tar.gz:

Publisher: release.yml on uninhibited-scholar/precheck-guardian

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file precheck_guardian-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for precheck_guardian-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 1d25b4ea4165ebf8d3a95914d76f0bc05f58a2c54c1ee060a7497e47f99e0bf5
MD5 43db14b39fca8acec098e2307f508be2
BLAKE2b-256 d38543f25fde4135f1002433f56259b4c755c098b4d38c4a656a8c357a5f5a69

See more details on using hashes here.

Provenance

The following attestation bundles were made for precheck_guardian-0.1.1-py3-none-any.whl:

Publisher: release.yml on uninhibited-scholar/precheck-guardian

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page