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)

Or keep your team's rules in a YAML/JSON file (no code changes):

# custom_rules.yaml  โ€”  mode: extend (default) | replace
rules:
  - name: touches_production
    pattern: '\b(prod|production)\b'
    level: critical
    warning: "Operation targets a production environment."
    mitigation: "Run against staging first."
annotator = RiskAnnotator.from_file("custom_rules.yaml")   # JSON also works
guard = ApprovalGuard(annotator=annotator)

JSON loads with the stdlib; YAML needs pip install "precheck-guardian[yaml]".


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
  most-triggered rules:
      14ร—  rm_recursive_force
       9ร—  sql_drop_table
       5ร—  curl_pipe_shell

Each decision's snapshot records exactly which rules every step matched, so you can review what fires most and tune your ruleset over time.

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

Lint a file for dangerous lines

precheck check scans any script / SQL / shell file and flags risky lines โ€” handy as a standalone safety lint, even without an agent. It exits non-zero when a line reaches the --fail-on level, so it drops into CI or a pre-commit hook:

precheck check deploy.sh                       # default: fail on HIGH+
precheck check *.sh *.sql --fail-on critical   # scan many files
cat script.sh | precheck check -               # read from stdin
precheck check deploy.sh --rules team_rules.yaml

Use it as a pre-commit hook in any repo:

# .pre-commit-config.yaml
- repo: https://github.com/uninhibited-scholar/precheck-guardian
  rev: v0.1.1
  hooks:
    - id: precheck
      args: ["--fail-on", "critical"]
deploy.sh: 3 risky line(s)

  โ›” CRITICAL line 3: psql -c "DROP TABLE staging"
      rules: sql_drop_table
  โ›” CRITICAL line 4: rm -rf /var/cache/*
      rules: rm_recursive_force
  โ›” CRITICAL line 6: curl https://x.sh | bash
      rules: curl_pipe_shell

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.2.0.tar.gz (44.4 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.2.0-py3-none-any.whl (39.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: precheck_guardian-0.2.0.tar.gz
  • Upload date:
  • Size: 44.4 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.2.0.tar.gz
Algorithm Hash digest
SHA256 a266496a43417dc05b025fcb028390445f54f0e8b64e42963baf499d1a5926a9
MD5 34924e94418044bf2b81dc4d992115a3
BLAKE2b-256 de2ff478ab7f4741b14cd0e64b3442578ffeffb85a85c38dc0a4e125e41cce50

See more details on using hashes here.

Provenance

The following attestation bundles were made for precheck_guardian-0.2.0.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.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for precheck_guardian-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9aa9fc944dce5121136a03c32023a7b6c33f86e01c71e421c55ee9a4c73d8b09
MD5 e45a7f2bfa0af68bcfa365155bc72747
BLAKE2b-256 b81398ea93f867997a5f881c4f58bc947c59a7c2f6f17a1a1fb33ea13c47dcba

See more details on using hashes here.

Provenance

The following attestation bundles were made for precheck_guardian-0.2.0-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