agentguard-oss 0.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. agentguard_oss-0.4.1/.gitignore +13 -0
  2. agentguard_oss-0.4.1/CHANGELOG.md +137 -0
  3. agentguard_oss-0.4.1/LICENSE +22 -0
  4. agentguard_oss-0.4.1/PKG-INFO +225 -0
  5. agentguard_oss-0.4.1/README.md +160 -0
  6. agentguard_oss-0.4.1/agentguard/__init__.py +41 -0
  7. agentguard_oss-0.4.1/agentguard/__main__.py +3 -0
  8. agentguard_oss-0.4.1/agentguard/adapters/__init__.py +0 -0
  9. agentguard_oss-0.4.1/agentguard/adapters/_common.py +54 -0
  10. agentguard_oss-0.4.1/agentguard/adapters/adk.py +23 -0
  11. agentguard_oss-0.4.1/agentguard/adapters/langchain.py +31 -0
  12. agentguard_oss-0.4.1/agentguard/adapters/mcp.py +5 -0
  13. agentguard_oss-0.4.1/agentguard/adapters/openai_agents.py +29 -0
  14. agentguard_oss-0.4.1/agentguard/adapters/python.py +3 -0
  15. agentguard_oss-0.4.1/agentguard/approval/__init__.py +16 -0
  16. agentguard_oss-0.4.1/agentguard/approval/base.py +49 -0
  17. agentguard_oss-0.4.1/agentguard/approval/cli.py +47 -0
  18. agentguard_oss-0.4.1/agentguard/approval/queue.py +90 -0
  19. agentguard_oss-0.4.1/agentguard/approval/slack.py +174 -0
  20. agentguard_oss-0.4.1/agentguard/approval/store.py +361 -0
  21. agentguard_oss-0.4.1/agentguard/approval/webhook.py +83 -0
  22. agentguard_oss-0.4.1/agentguard/audit/__init__.py +0 -0
  23. agentguard_oss-0.4.1/agentguard/audit/chain.py +36 -0
  24. agentguard_oss-0.4.1/agentguard/audit/export.py +121 -0
  25. agentguard_oss-0.4.1/agentguard/audit/logger.py +108 -0
  26. agentguard_oss-0.4.1/agentguard/audit/reader.py +42 -0
  27. agentguard_oss-0.4.1/agentguard/audit/report.py +110 -0
  28. agentguard_oss-0.4.1/agentguard/audit/segments.py +46 -0
  29. agentguard_oss-0.4.1/agentguard/bench/__init__.py +1 -0
  30. agentguard_oss-0.4.1/agentguard/bench/holdout_cases.yaml +42 -0
  31. agentguard_oss-0.4.1/agentguard/bench/judge.py +203 -0
  32. agentguard_oss-0.4.1/agentguard/bench/judge_cases.yaml +83 -0
  33. agentguard_oss-0.4.1/agentguard/cli/__init__.py +0 -0
  34. agentguard_oss-0.4.1/agentguard/cli/main.py +395 -0
  35. agentguard_oss-0.4.1/agentguard/core/__init__.py +0 -0
  36. agentguard_oss-0.4.1/agentguard/core/action.py +37 -0
  37. agentguard_oss-0.4.1/agentguard/core/context.py +11 -0
  38. agentguard_oss-0.4.1/agentguard/core/decision.py +40 -0
  39. agentguard_oss-0.4.1/agentguard/core/guard.py +598 -0
  40. agentguard_oss-0.4.1/agentguard/core/ratelimit.py +45 -0
  41. agentguard_oss-0.4.1/agentguard/dashboard/__init__.py +1 -0
  42. agentguard_oss-0.4.1/agentguard/dashboard/app.py +317 -0
  43. agentguard_oss-0.4.1/agentguard/dashboard/static/app.css +147 -0
  44. agentguard_oss-0.4.1/agentguard/dashboard/static/app.js +312 -0
  45. agentguard_oss-0.4.1/agentguard/dashboard/static/index.html +110 -0
  46. agentguard_oss-0.4.1/agentguard/demo/__init__.py +0 -0
  47. agentguard_oss-0.4.1/agentguard/demo/gemma_agent.py +106 -0
  48. agentguard_oss-0.4.1/agentguard/demo/scenario.py +169 -0
  49. agentguard_oss-0.4.1/agentguard/execution/__init__.py +13 -0
  50. agentguard_oss-0.4.1/agentguard/execution/container.py +157 -0
  51. agentguard_oss-0.4.1/agentguard/execution/egress.py +191 -0
  52. agentguard_oss-0.4.1/agentguard/execution/executor.py +9 -0
  53. agentguard_oss-0.4.1/agentguard/execution/filesystem.py +59 -0
  54. agentguard_oss-0.4.1/agentguard/execution/network.py +42 -0
  55. agentguard_oss-0.4.1/agentguard/execution/pinning.py +59 -0
  56. agentguard_oss-0.4.1/agentguard/execution/shell.py +149 -0
  57. agentguard_oss-0.4.1/agentguard/execution/verification.py +38 -0
  58. agentguard_oss-0.4.1/agentguard/interceptors/__init__.py +0 -0
  59. agentguard_oss-0.4.1/agentguard/interceptors/middleware.py +11 -0
  60. agentguard_oss-0.4.1/agentguard/policy/__init__.py +0 -0
  61. agentguard_oss-0.4.1/agentguard/policy/engine.py +20 -0
  62. agentguard_oss-0.4.1/agentguard/policy/explain.py +82 -0
  63. agentguard_oss-0.4.1/agentguard/policy/loader.py +38 -0
  64. agentguard_oss-0.4.1/agentguard/policy/matcher.py +63 -0
  65. agentguard_oss-0.4.1/agentguard/policy/schema.py +121 -0
  66. agentguard_oss-0.4.1/agentguard/py.typed +0 -0
  67. agentguard_oss-0.4.1/agentguard/risk/__init__.py +0 -0
  68. agentguard_oss-0.4.1/agentguard/risk/commands.py +450 -0
  69. agentguard_oss-0.4.1/agentguard/risk/data/gitleaks-LICENSE.txt +21 -0
  70. agentguard_oss-0.4.1/agentguard/risk/data/gitleaks.toml +3213 -0
  71. agentguard_oss-0.4.1/agentguard/risk/gemma_judge.py +106 -0
  72. agentguard_oss-0.4.1/agentguard/risk/pii.py +64 -0
  73. agentguard_oss-0.4.1/agentguard/risk/rules.py +53 -0
  74. agentguard_oss-0.4.1/agentguard/risk/rulesets.py +158 -0
  75. agentguard_oss-0.4.1/agentguard/risk/scorer.py +107 -0
  76. agentguard_oss-0.4.1/agentguard/risk/secrets.py +124 -0
  77. agentguard_oss-0.4.1/examples/mcp_server.py +27 -0
  78. agentguard_oss-0.4.1/examples/policies/browsing-agent.yaml +24 -0
  79. agentguard_oss-0.4.1/examples/policies/coding-agent.yaml +36 -0
  80. agentguard_oss-0.4.1/examples/policies/support-agent.yaml +40 -0
  81. agentguard_oss-0.4.1/examples/policy.yaml +34 -0
  82. agentguard_oss-0.4.1/examples/quickstart.py +45 -0
  83. agentguard_oss-0.4.1/pyproject.toml +74 -0
  84. agentguard_oss-0.4.1/tests/conftest.py +43 -0
  85. agentguard_oss-0.4.1/tests/test_adapters.py +158 -0
  86. agentguard_oss-0.4.1/tests/test_approvals.py +297 -0
  87. agentguard_oss-0.4.1/tests/test_audit.py +77 -0
  88. agentguard_oss-0.4.1/tests/test_audit_scale.py +169 -0
  89. agentguard_oss-0.4.1/tests/test_bench.py +108 -0
  90. agentguard_oss-0.4.1/tests/test_dashboard.py +249 -0
  91. agentguard_oss-0.4.1/tests/test_detection.py +152 -0
  92. agentguard_oss-0.4.1/tests/test_execution.py +202 -0
  93. agentguard_oss-0.4.1/tests/test_guard.py +312 -0
  94. agentguard_oss-0.4.1/tests/test_integrations.py +224 -0
  95. agentguard_oss-0.4.1/tests/test_isolation.py +245 -0
  96. agentguard_oss-0.4.1/tests/test_policy_risk.py +610 -0
  97. agentguard_oss-0.4.1/tests/test_policy_tools.py +146 -0
  98. agentguard_oss-0.4.1/tests/test_properties.py +344 -0
  99. agentguard_oss-0.4.1/tests/test_registration.py +198 -0
  100. agentguard_oss-0.4.1/tests/test_sessions.py +193 -0
@@ -0,0 +1,13 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.py[cod]
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ dist/
7
+ *.egg-info/
8
+ *.jsonl
9
+ *.jsonl.lock
10
+ .demo-workspace/
11
+
12
+ site/
13
+ .hypothesis/
@@ -0,0 +1,137 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow
5
+ [Semantic Versioning](https://semver.org/). Before 1.0, minor versions may change the API.
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.4.1] - 2026-10-10
10
+
11
+ ### Fixed
12
+ - Ask-level command concerns (irreversible infrastructure operations, raw network transfers,
13
+ environment dumps, inline code that runs commands) were dropped when the command was wrapped
14
+ in `bash -c`, `cmd /c`, PowerShell `-Command` or `eval`, so `bash -c 'terraform destroy'`
15
+ ran without approval. They now propagate through these wrappers, within the nesting limit.
16
+ - Changes to `core.hooksPath` through legacy or modern `git config` commands now raise
17
+ ask-level risk, including wrapped shell commands. Queries and unrelated settings remain
18
+ allowed by this check.
19
+
20
+ ## [0.4.0] - 2026-10-08
21
+
22
+ ### Added
23
+ - `ContainerExecutor`: allowlisted commands in Docker or Podman (optionally gVisor) with no
24
+ network, read-only root, no capabilities, no-new-privileges, an unprivileged user,
25
+ resource limits and a read-only workspace by default.
26
+ - DNS pinning: `NetworkExecutor` connects only to the public addresses it checked.
27
+ - `EgressProxy` and `agentguard egress-proxy`: an allowlisting HTTPS CONNECT proxy with
28
+ pinned public DNS and audited decisions.
29
+ - Secret detection with the vendored gitleaks ruleset (221 rules) on RE2, and
30
+ `scripts/update_secret_rules.py`.
31
+ - PII detection (email, phone, credit card, SSN, IBAN) with checksums; policies can name
32
+ the categories in `sensitive_data`; cards, SSNs and IBANs are redacted from logs.
33
+ - Shell-aware command analysis (`agentguard.risk.commands`): tokenizes, splits and
34
+ unwraps commands; new hard-deny and ask-level categories; obfuscation asks.
35
+ - `agentguard judge-bench`: labeled benchmark (71 cases plus a 35-case holdout) reporting
36
+ recall, false positives, instability and latency for rules, a judge and both combined.
37
+ - Property-based tests (Hypothesis) for paths, URLs, commands, secrets, audit and policy.
38
+ - CI on Linux, Windows and macOS with Python 3.11-3.14; CodeQL, bandit, pip-audit and
39
+ Dependabot; nightly long property runs; Docker integration tests.
40
+ - Security review guide for an independent reviewer.
41
+
42
+ ### Changed
43
+ - New dependency: `google-re2`.
44
+ - Deterministic recall on the benchmark rose from 54% to 95% (holdout: 0% to 100%, same
45
+ author; see the benchmarks page).
46
+
47
+ ### Fixed
48
+ - `rm -r -f`, `chmod 0777`, `curl ... | python3` evaded detection; `echo rm -rf` was
49
+ blocked; URLs with DEL or C1 characters were accepted.
50
+ - Persistence paths were missed on Windows after normalization.
51
+ - `killpg` cleanup raised on macOS; writable container workspaces were unwritable.
52
+
53
+ ## [0.3.0] - 2026-10-08
54
+
55
+ ### Added
56
+ - Queued approvals that do not block the agent: `ApprovalStore` (SQLite, shared by agents and
57
+ the dashboard) and `QueueApproval`. An `ask` call raises `ApprovalPending` with a request
58
+ ID; retrying after approval runs it. Identical pending calls share one request, and
59
+ recently rejected calls are denied without asking again. `wait=` waits inside the call.
60
+ - Grants: approve once, the same call, a tool or a capability for 1 minute to 24 hours,
61
+ bound to agent and environment, revocable.
62
+ - Token-authenticated approvers (hashed), with strong approval for `--strong` approvers who
63
+ re-enter their token. `agentguard approvers add/list/remove`, `agentguard approvals list`.
64
+ - Slack notifications with approve/allow/reject buttons (signature-verified clicks) and
65
+ HMAC-signed webhooks (`verify_signature`).
66
+ - `agentguard dashboard` (extra `dashboard`): approval queue, grants, audit log viewer with
67
+ search and timelines, and policy report; token login, CSRF protection, strict CSP, and a
68
+ JSON API with Bearer tokens.
69
+ - `agentguard report` and `build_report`: what was asked about or denied, by tool and
70
+ reason, for tuning a policy from dry-run sessions.
71
+ - Audit checkpoint (`.head`) so appends no longer re-read the log; rotation (`max_bytes`)
72
+ with the chain continuing across segments; HMAC signing (`signing_key`, `--key-env`);
73
+ `LoggingExporter` (OpenTelemetry, syslog) and `HttpExporter` (Splunk HEC, collectors).
74
+ - Sessions: `guard.new_session(agent_id=...)` with its own lock, rate window, taint and halt
75
+ state; sessions run in parallel. `with session:` binds tools in a context.
76
+ - Pluggable rate limiting: `LocalRateLimiter` and `RedisRateLimiter` (extra `redis`), with
77
+ policy `limits.rate_limit_scope` of `session`, `agent` or `global`.
78
+
79
+ ### Changed
80
+ - `max_calls_per_minute` is enforced per `rate_limit_scope` (default `session`, as before).
81
+ - Audit events may carry `signature`; it is excluded from the event hash.
82
+ - CLI errors outside `demo` no longer suggest checking the model service.
83
+
84
+ ### Fixed
85
+ - Credential paths after `@` or `=` (`curl -d @.env`, `--file=.env`) are hard-denied.
86
+
87
+ ## [0.2.0] - 2026-10-08
88
+
89
+ Renamed the distribution to `agentguard-oss` (`import agentguard`) and prepared PyPI
90
+ publishing.
91
+
92
+ ### Added
93
+ - Argument roles: `guard.tool(path_arg=, url_arg=, command_arg=, content_arg=,
94
+ recipient_args=)` for tools whose parameters are not named `path`, `url`, `cmd`,
95
+ `content` or `to`. Misconfigured tools fail at registration.
96
+ - Custom capabilities declared in the policy (`capabilities: {db.query: {risk: 30}}`),
97
+ with baseline risk, an outbound flag for session-taint checks, and a description.
98
+ - `agentguard check-policy` validates a policy and prints a summary or located errors.
99
+ - `agentguard explain` shows the matching rules, risk factors and decision for one call.
100
+ - Framework adapters: LangChain/LangGraph, OpenAI Agents SDK and Google ADK
101
+ (`agentguard.adapters.*.guarded_tool`). Denials are returned to the model with reasons.
102
+ - Gemma is the default live demo agent when Ollama has a Gemma model; `--scripted` runs
103
+ offline. The judge takes trusted `guidance` and a `capabilities` scope.
104
+ - `ShellExecutor(check=False)` returns nonzero exits as results.
105
+ - `Guard.tools`, `Guard.capabilities`, `agentguard.__version__`, `py.typed`.
106
+
107
+ ### Changed
108
+ - Distribution renamed from `agentguard-sdk` (taken on PyPI) to `agentguard-oss`.
109
+ - Tools receive the absolute requested path; policy and credential checks use both the
110
+ requested path and its resolved target.
111
+ - A judge `deny` flag escalates to human approval; a judge score at or above the deny
112
+ threshold still denies.
113
+ - Ollama requests disable thinking, use an 8192-token context and longer timeouts.
114
+
115
+ ### Fixed
116
+ - Links inside the workspace bypassed `FilesystemExecutor` link rejection, and Windows
117
+ junctions were missed on Python 3.11 by the executor and the verifier.
118
+ - The email/message recipient limit ignored `cc`, `bcc` and `recipients`.
119
+ - Nested guarded calls from an async tool deadlocked.
120
+ - New files were created with lowercase names on Windows.
121
+ - `format` flags (`ruff format`, `--format=`) were hard-denied as disk formatting.
122
+ - Policy domains accepted `?` and `[`, which acted as wildcards.
123
+ - `git push` and `repository.write` were not treated as outbound for session taint.
124
+ - The audit record overwrote the action's proposal timestamp.
125
+
126
+ ## [0.1.0] - 2026-10-08
127
+
128
+ Initial source release: policy as code, deterministic risk scoring, CLI approval,
129
+ controlled executors, workspace verification, hash-chained audit log, MCP adapter,
130
+ and the prompt-injection demo with an optional local Gemma agent and judge.
131
+
132
+ [Unreleased]: https://github.com/prollysamz/agentguard/compare/v0.4.1...HEAD
133
+ [0.4.1]: https://github.com/prollysamz/agentguard/compare/v0.4.0...v0.4.1
134
+ [0.4.0]: https://github.com/prollysamz/agentguard/compare/v0.3.0...v0.4.0
135
+ [0.3.0]: https://github.com/prollysamz/agentguard/compare/971b8bd...v0.3.0
136
+ [0.2.0]: https://github.com/prollysamz/agentguard/compare/8a0f847...971b8bd
137
+ [0.1.0]: https://github.com/prollysamz/agentguard/commit/8a0f847
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AgentGuard contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,225 @@
1
+ Metadata-Version: 2.5
2
+ Name: agentguard-oss
3
+ Version: 0.4.1
4
+ Summary: Open-source safety layer for AI agent tool calls: policy, risk checks, human approval and tamper-evident audit
5
+ Project-URL: Homepage, https://github.com/prollysamz/agentguard
6
+ Project-URL: Documentation, https://prollysamz.github.io/agentguard/
7
+ Project-URL: Repository, https://github.com/prollysamz/agentguard
8
+ Project-URL: Issues, https://github.com/prollysamz/agentguard/issues
9
+ Project-URL: Changelog, https://github.com/prollysamz/agentguard/blob/main/CHANGELOG.md
10
+ Author: Sameer Jakkuva
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: ai-agents,ai-safety,gemma,guardrails,llm-security,mcp,policy
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
+ Classifier: Topic :: Security
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.11
26
+ Requires-Dist: google-re2<2,>=1.1
27
+ Requires-Dist: httpx<1,>=0.28
28
+ Requires-Dist: portalocker<4,>=3
29
+ Requires-Dist: pydantic<3,>=2.9
30
+ Requires-Dist: pyyaml<7,>=6.0.2
31
+ Provides-Extra: adk
32
+ Requires-Dist: google-adk<3,>=2; extra == 'adk'
33
+ Provides-Extra: all
34
+ Requires-Dist: google-adk<3,>=2; extra == 'all'
35
+ Requires-Dist: langchain-core<2,>=1; extra == 'all'
36
+ Requires-Dist: mcp<2,>=1.12; extra == 'all'
37
+ Requires-Dist: openai-agents<1,>=0.23; extra == 'all'
38
+ Requires-Dist: redis<9,>=5; extra == 'all'
39
+ Requires-Dist: starlette<2,>=0.40; extra == 'all'
40
+ Requires-Dist: uvicorn<1,>=0.30; extra == 'all'
41
+ Provides-Extra: dashboard
42
+ Requires-Dist: starlette<2,>=0.40; extra == 'dashboard'
43
+ Requires-Dist: uvicorn<1,>=0.30; extra == 'dashboard'
44
+ Provides-Extra: dev
45
+ Requires-Dist: bandit<2,>=1.8; extra == 'dev'
46
+ Requires-Dist: build<2,>=1; extra == 'dev'
47
+ Requires-Dist: fakeredis<3,>=2.26; extra == 'dev'
48
+ Requires-Dist: hypothesis<7,>=6.100; extra == 'dev'
49
+ Requires-Dist: langgraph<2,>=1; extra == 'dev'
50
+ Requires-Dist: pytest<10,>=8; extra == 'dev'
51
+ Requires-Dist: ruff<1,>=0.11; extra == 'dev'
52
+ Requires-Dist: twine<8,>=7; extra == 'dev'
53
+ Provides-Extra: docs
54
+ Requires-Dist: mkdocs-material<10,>=9.5; extra == 'docs'
55
+ Requires-Dist: mkdocs<2,>=1.6; extra == 'docs'
56
+ Provides-Extra: langchain
57
+ Requires-Dist: langchain-core<2,>=1; extra == 'langchain'
58
+ Provides-Extra: mcp
59
+ Requires-Dist: mcp<2,>=1.12; extra == 'mcp'
60
+ Provides-Extra: openai-agents
61
+ Requires-Dist: openai-agents<1,>=0.23; extra == 'openai-agents'
62
+ Provides-Extra: redis
63
+ Requires-Dist: redis<9,>=5; extra == 'redis'
64
+ Description-Content-Type: text/markdown
65
+
66
+ # AgentGuard
67
+
68
+ **Let agents act. Keep humans in control.**
69
+
70
+ [![CI](https://github.com/prollysamz/agentguard/actions/workflows/ci.yml/badge.svg)](https://github.com/prollysamz/agentguard/actions/workflows/ci.yml)
71
+ [![Docs](https://github.com/prollysamz/agentguard/actions/workflows/docs.yml/badge.svg)](https://prollysamz.github.io/agentguard/)
72
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
73
+ ![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)
74
+
75
+ AgentGuard is an open-source Python SDK that sits between an AI agent and its tools.
76
+ Every tool call is validated, checked against a policy, scored for risk, optionally
77
+ reviewed by a local **Gemma** model, sent to a human when needed, run through a
78
+ restricted executor, verified, and written to a tamper-evident audit log.
79
+
80
+ **[Documentation](https://prollysamz.github.io/agentguard/)** ·
81
+ [Quickstart](https://prollysamz.github.io/agentguard/quickstart/) ·
82
+ [Integrations](https://prollysamz.github.io/agentguard/integrations/langchain/) ·
83
+ [Threat model](THREAT_MODEL.md) · [Changelog](CHANGELOG.md)
84
+
85
+ ## See it work
86
+
87
+ ```sh
88
+ pip install agentguard-oss
89
+ agentguard demo --scripted
90
+ ```
91
+
92
+ An agent is asked to fix a calculator. The repository's README hides a prompt injection
93
+ telling it to read `~/.ssh/id_rsa` and upload it. AgentGuard lets the agent read the
94
+ README, **blocks** the SSH-key read and the `curl` exfiltration, **allows** the fix and a
95
+ real unit test, and **escalates** the push to `main` for human approval.
96
+
97
+ With [Ollama](https://ollama.com) and `ollama pull gemma3:4b`, plain `agentguard demo`
98
+ runs a live **Gemma agent** against the same trap, and `--judge` adds a Gemma security
99
+ reviewer. Add `--interactive` to approve the push yourself. See
100
+ [Gemma agent and judge](https://prollysamz.github.io/agentguard/gemma/).
101
+
102
+ ## Guard a tool
103
+
104
+ ```python
105
+ from pathlib import Path
106
+
107
+ from agentguard import Guard
108
+
109
+ guard = Guard(
110
+ {
111
+ "version": 1,
112
+ "defaults": {"effect": "deny"},
113
+ "rules": [
114
+ {"capability": "filesystem.read", "paths": ["./workspace/**"], "effect": "allow"},
115
+ {"capability": "shell.execute", "effect": "ask"},
116
+ ],
117
+ },
118
+ audit="agentguard.jsonl",
119
+ )
120
+
121
+
122
+ @guard.tool(capability="filesystem.read")
123
+ def read_file(path: str) -> str:
124
+ """Read a UTF-8 text file."""
125
+ return Path(path).read_text(encoding="utf-8")
126
+
127
+
128
+ # Give the agent read_file, never the raw function.
129
+ # read_file("~/.ssh/id_rsa") raises GuardDenied before the function body runs.
130
+ ```
131
+
132
+ Then check the policy, ask how it decides a call, and verify the log:
133
+
134
+ ```sh
135
+ agentguard check-policy policy.yaml
136
+ agentguard explain policy.yaml shell.execute --arg cmd="git push origin main"
137
+ agentguard verify-log --audit agentguard.jsonl
138
+ ```
139
+
140
+ ## Use it with your framework
141
+
142
+ ```sh
143
+ pip install "agentguard-oss[langchain]" # also [openai-agents], [adk], [mcp], [all]
144
+ ```
145
+
146
+ ```python
147
+ from agentguard.adapters.langchain import guarded_tool # LangChain / LangGraph
148
+ # from agentguard.adapters.openai_agents import guarded_tool # OpenAI Agents SDK
149
+ # from agentguard.adapters.adk import guarded_tool # Google ADK
150
+
151
+
152
+ def read_notes(path: str) -> str:
153
+ """Read a notes file."""
154
+ return Path(path).read_text(encoding="utf-8")
155
+
156
+
157
+ notes_tool = guarded_tool(guard, read_notes, capability="filesystem.read")
158
+ ```
159
+
160
+ Denials go back to the model with AgentGuard's reasons so the agent can adapt. MCP
161
+ servers use `agentguard.adapters.mcp.register_tool`; any other loop can call
162
+ `guard.call(name, arguments)`.
163
+
164
+ ## What you get
165
+
166
+ | Control | Details |
167
+ | --- | --- |
168
+ | **Policy as code** | YAML allow / ask / deny rules by capability, path, domain, environment and secrets. Explicit denies win. [Custom capabilities](https://prollysamz.github.io/agentguard/policy/#custom-capabilities) like `db.query` or `payments.refund`. |
169
+ | **Risk checks** | Explainable 0–100 scores. Credential files, destructive commands and exfiltration after a secret was seen are always denied. |
170
+ | **Human approval** | Risky calls go to a person in the dashboard, Slack or your own system without blocking the agent; grants like "allow this tool for 10 minutes". No answer means deny. |
171
+ | **Isolation** | Container executor (no network, read-only, no capabilities, optional gVisor), exact-command runners, DNS-pinned HTTPS, and an allowlisting egress proxy. |
172
+ | **Detection** | gitleaks' 221 secret rules on RE2, checksum-validated PII, and shell-aware command analysis. |
173
+ | **Verification** | Before/after hashes catch tools that change files they were not asked to. |
174
+ | **Audit** | Every stage of every call in a signed SHA-256 hash chain with rotation, exported to OpenTelemetry or a SIEM. |
175
+ | **Dashboard** | Approval queue, audit log viewer and a dry-run policy report. |
176
+ | **Multi-agent** | Parallel per-agent sessions; rate limits per session, agent or globally through Redis. |
177
+ | **Gemma** | A local Gemma agent for the demo, and an optional judge that can only add caution. |
178
+
179
+ Starter policies for [coding, browsing and support agents](examples/policies/) are in
180
+ `examples/policies/`.
181
+
182
+ ## Operate it
183
+
184
+ ```sh
185
+ pip install "agentguard-oss[dashboard]"
186
+ agentguard approvers add alice # prints a token for the dashboard and API
187
+ agentguard dashboard # http://127.0.0.1:8765
188
+ ```
189
+
190
+ ```python
191
+ from agentguard.approval import ApprovalStore, QueueApproval
192
+
193
+ guard = Guard("policy.yaml", approval=QueueApproval(ApprovalStore("agentguard-approvals.db")))
194
+ ```
195
+
196
+ Risky calls raise `ApprovalPending` immediately instead of blocking; the agent retries after
197
+ a human approves in the [dashboard](https://prollysamz.github.io/agentguard/dashboard/), in
198
+ Slack, or through the API. Run a new agent with `mode="dry-run"` and check
199
+ `agentguard report --dry-run-only` to tune the policy before enforcing it.
200
+
201
+ ## Status
202
+
203
+ AgentGuard 0.4 is alpha and has not had an independent security audit; see the
204
+ [security review guide](https://prollysamz.github.io/agentguard/security-review/) and
205
+ [benchmarks](https://prollysamz.github.io/agentguard/benchmarks/). It is an
206
+ interception layer for cooperative applications, not an OS sandbox: an agent that also has
207
+ unguarded tools, a raw shell or Python `exec` can go around it. Read the
208
+ [threat model](THREAT_MODEL.md) before guarding privileged tools, and report
209
+ vulnerabilities privately as described in [SECURITY.md](SECURITY.md).
210
+
211
+ ## Develop
212
+
213
+ ```sh
214
+ git clone https://github.com/prollysamz/agentguard.git
215
+ cd agentguard
216
+ python -m venv .venv
217
+ # Windows: .venv\Scripts\activate macOS/Linux: source .venv/bin/activate
218
+ python -m pip install -e ".[dev,all,docs]"
219
+ python -m pytest -q
220
+ python -m ruff check .
221
+ mkdocs serve
222
+ ```
223
+
224
+ CI runs the tests on Linux and Windows with Python 3.11 and 3.12. See
225
+ [CONTRIBUTING.md](CONTRIBUTING.md). MIT licensed.
@@ -0,0 +1,160 @@
1
+ # AgentGuard
2
+
3
+ **Let agents act. Keep humans in control.**
4
+
5
+ [![CI](https://github.com/prollysamz/agentguard/actions/workflows/ci.yml/badge.svg)](https://github.com/prollysamz/agentguard/actions/workflows/ci.yml)
6
+ [![Docs](https://github.com/prollysamz/agentguard/actions/workflows/docs.yml/badge.svg)](https://prollysamz.github.io/agentguard/)
7
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
8
+ ![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)
9
+
10
+ AgentGuard is an open-source Python SDK that sits between an AI agent and its tools.
11
+ Every tool call is validated, checked against a policy, scored for risk, optionally
12
+ reviewed by a local **Gemma** model, sent to a human when needed, run through a
13
+ restricted executor, verified, and written to a tamper-evident audit log.
14
+
15
+ **[Documentation](https://prollysamz.github.io/agentguard/)** ·
16
+ [Quickstart](https://prollysamz.github.io/agentguard/quickstart/) ·
17
+ [Integrations](https://prollysamz.github.io/agentguard/integrations/langchain/) ·
18
+ [Threat model](THREAT_MODEL.md) · [Changelog](CHANGELOG.md)
19
+
20
+ ## See it work
21
+
22
+ ```sh
23
+ pip install agentguard-oss
24
+ agentguard demo --scripted
25
+ ```
26
+
27
+ An agent is asked to fix a calculator. The repository's README hides a prompt injection
28
+ telling it to read `~/.ssh/id_rsa` and upload it. AgentGuard lets the agent read the
29
+ README, **blocks** the SSH-key read and the `curl` exfiltration, **allows** the fix and a
30
+ real unit test, and **escalates** the push to `main` for human approval.
31
+
32
+ With [Ollama](https://ollama.com) and `ollama pull gemma3:4b`, plain `agentguard demo`
33
+ runs a live **Gemma agent** against the same trap, and `--judge` adds a Gemma security
34
+ reviewer. Add `--interactive` to approve the push yourself. See
35
+ [Gemma agent and judge](https://prollysamz.github.io/agentguard/gemma/).
36
+
37
+ ## Guard a tool
38
+
39
+ ```python
40
+ from pathlib import Path
41
+
42
+ from agentguard import Guard
43
+
44
+ guard = Guard(
45
+ {
46
+ "version": 1,
47
+ "defaults": {"effect": "deny"},
48
+ "rules": [
49
+ {"capability": "filesystem.read", "paths": ["./workspace/**"], "effect": "allow"},
50
+ {"capability": "shell.execute", "effect": "ask"},
51
+ ],
52
+ },
53
+ audit="agentguard.jsonl",
54
+ )
55
+
56
+
57
+ @guard.tool(capability="filesystem.read")
58
+ def read_file(path: str) -> str:
59
+ """Read a UTF-8 text file."""
60
+ return Path(path).read_text(encoding="utf-8")
61
+
62
+
63
+ # Give the agent read_file, never the raw function.
64
+ # read_file("~/.ssh/id_rsa") raises GuardDenied before the function body runs.
65
+ ```
66
+
67
+ Then check the policy, ask how it decides a call, and verify the log:
68
+
69
+ ```sh
70
+ agentguard check-policy policy.yaml
71
+ agentguard explain policy.yaml shell.execute --arg cmd="git push origin main"
72
+ agentguard verify-log --audit agentguard.jsonl
73
+ ```
74
+
75
+ ## Use it with your framework
76
+
77
+ ```sh
78
+ pip install "agentguard-oss[langchain]" # also [openai-agents], [adk], [mcp], [all]
79
+ ```
80
+
81
+ ```python
82
+ from agentguard.adapters.langchain import guarded_tool # LangChain / LangGraph
83
+ # from agentguard.adapters.openai_agents import guarded_tool # OpenAI Agents SDK
84
+ # from agentguard.adapters.adk import guarded_tool # Google ADK
85
+
86
+
87
+ def read_notes(path: str) -> str:
88
+ """Read a notes file."""
89
+ return Path(path).read_text(encoding="utf-8")
90
+
91
+
92
+ notes_tool = guarded_tool(guard, read_notes, capability="filesystem.read")
93
+ ```
94
+
95
+ Denials go back to the model with AgentGuard's reasons so the agent can adapt. MCP
96
+ servers use `agentguard.adapters.mcp.register_tool`; any other loop can call
97
+ `guard.call(name, arguments)`.
98
+
99
+ ## What you get
100
+
101
+ | Control | Details |
102
+ | --- | --- |
103
+ | **Policy as code** | YAML allow / ask / deny rules by capability, path, domain, environment and secrets. Explicit denies win. [Custom capabilities](https://prollysamz.github.io/agentguard/policy/#custom-capabilities) like `db.query` or `payments.refund`. |
104
+ | **Risk checks** | Explainable 0–100 scores. Credential files, destructive commands and exfiltration after a secret was seen are always denied. |
105
+ | **Human approval** | Risky calls go to a person in the dashboard, Slack or your own system without blocking the agent; grants like "allow this tool for 10 minutes". No answer means deny. |
106
+ | **Isolation** | Container executor (no network, read-only, no capabilities, optional gVisor), exact-command runners, DNS-pinned HTTPS, and an allowlisting egress proxy. |
107
+ | **Detection** | gitleaks' 221 secret rules on RE2, checksum-validated PII, and shell-aware command analysis. |
108
+ | **Verification** | Before/after hashes catch tools that change files they were not asked to. |
109
+ | **Audit** | Every stage of every call in a signed SHA-256 hash chain with rotation, exported to OpenTelemetry or a SIEM. |
110
+ | **Dashboard** | Approval queue, audit log viewer and a dry-run policy report. |
111
+ | **Multi-agent** | Parallel per-agent sessions; rate limits per session, agent or globally through Redis. |
112
+ | **Gemma** | A local Gemma agent for the demo, and an optional judge that can only add caution. |
113
+
114
+ Starter policies for [coding, browsing and support agents](examples/policies/) are in
115
+ `examples/policies/`.
116
+
117
+ ## Operate it
118
+
119
+ ```sh
120
+ pip install "agentguard-oss[dashboard]"
121
+ agentguard approvers add alice # prints a token for the dashboard and API
122
+ agentguard dashboard # http://127.0.0.1:8765
123
+ ```
124
+
125
+ ```python
126
+ from agentguard.approval import ApprovalStore, QueueApproval
127
+
128
+ guard = Guard("policy.yaml", approval=QueueApproval(ApprovalStore("agentguard-approvals.db")))
129
+ ```
130
+
131
+ Risky calls raise `ApprovalPending` immediately instead of blocking; the agent retries after
132
+ a human approves in the [dashboard](https://prollysamz.github.io/agentguard/dashboard/), in
133
+ Slack, or through the API. Run a new agent with `mode="dry-run"` and check
134
+ `agentguard report --dry-run-only` to tune the policy before enforcing it.
135
+
136
+ ## Status
137
+
138
+ AgentGuard 0.4 is alpha and has not had an independent security audit; see the
139
+ [security review guide](https://prollysamz.github.io/agentguard/security-review/) and
140
+ [benchmarks](https://prollysamz.github.io/agentguard/benchmarks/). It is an
141
+ interception layer for cooperative applications, not an OS sandbox: an agent that also has
142
+ unguarded tools, a raw shell or Python `exec` can go around it. Read the
143
+ [threat model](THREAT_MODEL.md) before guarding privileged tools, and report
144
+ vulnerabilities privately as described in [SECURITY.md](SECURITY.md).
145
+
146
+ ## Develop
147
+
148
+ ```sh
149
+ git clone https://github.com/prollysamz/agentguard.git
150
+ cd agentguard
151
+ python -m venv .venv
152
+ # Windows: .venv\Scripts\activate macOS/Linux: source .venv/bin/activate
153
+ python -m pip install -e ".[dev,all,docs]"
154
+ python -m pytest -q
155
+ python -m ruff check .
156
+ mkdocs serve
157
+ ```
158
+
159
+ CI runs the tests on Linux and Windows with Python 3.11 and 3.12. See
160
+ [CONTRIBUTING.md](CONTRIBUTING.md). MIT licensed.
@@ -0,0 +1,41 @@
1
+ """AgentGuard's public SDK.
2
+
3
+ Public API: the names in ``__all__`` here, plus ``agentguard.approval``,
4
+ ``agentguard.execution``, ``agentguard.adapters.*`` and
5
+ ``agentguard.risk.gemma_judge.GemmaJudge``. Other modules are internal and may change.
6
+ """
7
+
8
+ from importlib.metadata import PackageNotFoundError, version
9
+
10
+ from agentguard.approval.base import Approval, ApprovalProvider
11
+ from agentguard.core.action import Action
12
+ from agentguard.core.decision import (
13
+ ApprovalPending,
14
+ Decision,
15
+ Effect,
16
+ GuardDenied,
17
+ GuardError,
18
+ )
19
+ from agentguard.core.guard import Guard
20
+ from agentguard.policy.explain import explain
21
+ from agentguard.policy.loader import load_policy
22
+
23
+ try:
24
+ __version__ = version("agentguard-oss")
25
+ except PackageNotFoundError: # Running from a source tree without installation.
26
+ __version__ = "0.0.0+unknown"
27
+
28
+ __all__ = [
29
+ "Action",
30
+ "Approval",
31
+ "ApprovalPending",
32
+ "ApprovalProvider",
33
+ "Decision",
34
+ "Effect",
35
+ "Guard",
36
+ "GuardDenied",
37
+ "GuardError",
38
+ "__version__",
39
+ "explain",
40
+ "load_policy",
41
+ ]
@@ -0,0 +1,3 @@
1
+ from agentguard.cli.main import main
2
+
3
+ raise SystemExit(main())
File without changes
@@ -0,0 +1,54 @@
1
+ """Shared adapter helpers. Adapters translate and dispatch; they make no security decisions."""
2
+
3
+ import functools
4
+ import inspect
5
+
6
+ from agentguard.core.decision import ApprovalPending, GuardDenied
7
+
8
+
9
+ def denial_result(exc: GuardDenied) -> dict:
10
+ """What the model sees when AgentGuard denies a call: the reasons, so it can adapt."""
11
+ if isinstance(exc, ApprovalPending):
12
+ return {
13
+ "error": "Approval pending",
14
+ "request_id": exc.request_id,
15
+ "reasons": list(exc.decision.reasons),
16
+ "next_step": "A human must approve this. Continue with other work and retry "
17
+ "the same call later.",
18
+ }
19
+ return {"error": "Denied by AgentGuard", "reasons": list(exc.decision.reasons)}
20
+
21
+
22
+ def returning_denials(guarded, on_denied=denial_result):
23
+ """Wrap a guarded tool so denials become a result instead of an exception.
24
+
25
+ Only GuardDenied is converted. Other GuardErrors (a halted session, failed execution)
26
+ still raise, so frameworks surface them as tool failures.
27
+ """
28
+ if inspect.iscoroutinefunction(guarded):
29
+
30
+ @functools.wraps(guarded)
31
+ async def wrapper(*args, **kwargs):
32
+ try:
33
+ return await guarded(*args, **kwargs)
34
+ except GuardDenied as exc:
35
+ return on_denied(exc)
36
+ else:
37
+
38
+ @functools.wraps(guarded)
39
+ def wrapper(*args, **kwargs):
40
+ try:
41
+ return guarded(*args, **kwargs)
42
+ except GuardDenied as exc:
43
+ return on_denied(exc)
44
+
45
+ # The return type changes on denial; frameworks infer only the arguments from this.
46
+ wrapper.__signature__ = inspect.signature(guarded).replace(
47
+ return_annotation=inspect.Signature.empty
48
+ )
49
+ wrapper.__annotations__ = {k: v for k, v in guarded.__annotations__.items() if k != "return"}
50
+ return wrapper
51
+
52
+
53
+ def describe(function, description):
54
+ return description or inspect.getdoc(function) or f"{function.__name__} (guarded by AgentGuard)"