agent-guard-python 0.2.1__cp310-abi3-win_amd64.whl
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.
- agent_guard/__init__.py +43 -0
- agent_guard/_agent_guard.pyd +0 -0
- agent_guard/adapters.py +410 -0
- agent_guard/langchain.py +160 -0
- agent_guard/openai.py +87 -0
- agent_guard_python-0.2.1.dist-info/METADATA +119 -0
- agent_guard_python-0.2.1.dist-info/RECORD +9 -0
- agent_guard_python-0.2.1.dist-info/WHEEL +4 -0
- agent_guard_python-0.2.1.dist-info/sboms/agent-guard-python.cyclonedx.json +7620 -0
agent_guard/openai.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from typing import Any, Callable, Optional
|
|
2
|
+
|
|
3
|
+
from ._agent_guard import Guard
|
|
4
|
+
from .adapters import (
|
|
5
|
+
build_security_error,
|
|
6
|
+
dispatch_via_run,
|
|
7
|
+
ensure_verified_policy,
|
|
8
|
+
handle_execute_result,
|
|
9
|
+
has_runtime_api,
|
|
10
|
+
prepare_payload,
|
|
11
|
+
resolve_mode,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def wrap_openai_tool(
|
|
16
|
+
guard: Guard,
|
|
17
|
+
handler: Callable[[Any], Any],
|
|
18
|
+
*,
|
|
19
|
+
tool: str,
|
|
20
|
+
mode: str = "check",
|
|
21
|
+
trust_level: str = "untrusted",
|
|
22
|
+
agent_id: Optional[str] = None,
|
|
23
|
+
actor: Optional[str] = None,
|
|
24
|
+
result_mapper: Optional[Callable[[Any, Any], Any]] = None,
|
|
25
|
+
payload_mapper: Optional[Callable[[Any], str]] = None,
|
|
26
|
+
):
|
|
27
|
+
"""
|
|
28
|
+
Wrap an OpenAI-style tool handler with agent-guard checks.
|
|
29
|
+
|
|
30
|
+
The wrapped callable accepts the original handler input and dispatches
|
|
31
|
+
according to ``mode``:
|
|
32
|
+
|
|
33
|
+
- ``"enforce"`` — always sandbox the call via ``Guard.execute``.
|
|
34
|
+
- ``"check"`` — always go through ``Guard.check`` (policy-only). The
|
|
35
|
+
original handler runs in-process when allowed. Fail-closed on invalid
|
|
36
|
+
policy signatures.
|
|
37
|
+
- ``"auto"`` — for shell-like tools, behave like ``enforce``. For non-shell
|
|
38
|
+
tools, dispatch through the unified ``Guard.run`` runtime API when the
|
|
39
|
+
binding exposes it. The ``RuntimeOutcome::Handoff`` variant means the
|
|
40
|
+
handler runs in-process and the adapter then calls
|
|
41
|
+
``Guard.report_handoff_result`` to close the audit loop. When the binding
|
|
42
|
+
does not expose ``run`` (older builds), ``auto`` for non-shell tools
|
|
43
|
+
degrades to ``check`` semantics.
|
|
44
|
+
"""
|
|
45
|
+
if not callable(handler):
|
|
46
|
+
raise TypeError("wrap_openai_tool requires a callable handler")
|
|
47
|
+
|
|
48
|
+
resolved_mode = resolve_mode(tool, mode)
|
|
49
|
+
|
|
50
|
+
if resolved_mode == "run" and not has_runtime_api(guard):
|
|
51
|
+
resolved_mode = "check"
|
|
52
|
+
|
|
53
|
+
guard_options = {
|
|
54
|
+
"agent_id": agent_id,
|
|
55
|
+
"actor": actor,
|
|
56
|
+
"trust_level": trust_level,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
def wrapped(input_data: Any, *args, **kwargs):
|
|
60
|
+
payload = payload_mapper(input_data) if callable(payload_mapper) else prepare_payload(tool, input_data)
|
|
61
|
+
|
|
62
|
+
if resolved_mode == "enforce":
|
|
63
|
+
result = guard.execute(tool=tool, payload=payload, **guard_options)
|
|
64
|
+
return handle_execute_result(
|
|
65
|
+
result,
|
|
66
|
+
result_mapper=result_mapper,
|
|
67
|
+
original_input=input_data,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if resolved_mode == "run":
|
|
71
|
+
return dispatch_via_run(
|
|
72
|
+
guard,
|
|
73
|
+
tool=tool,
|
|
74
|
+
payload=payload,
|
|
75
|
+
guard_options=guard_options,
|
|
76
|
+
handler=handler,
|
|
77
|
+
handler_args=(input_data, *args),
|
|
78
|
+
handler_kwargs=kwargs,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
decision = guard.check(tool=tool, payload=payload, **guard_options)
|
|
82
|
+
if decision.outcome != "allow":
|
|
83
|
+
raise build_security_error(decision)
|
|
84
|
+
ensure_verified_policy(decision)
|
|
85
|
+
return handler(input_data, *args, **kwargs)
|
|
86
|
+
|
|
87
|
+
return wrapped
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agent-guard-python
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
7
|
+
Classifier: Topic :: Security
|
|
8
|
+
Summary: Outbound change control for AI agents — gate shell, file-write and HTTP side effects before they happen
|
|
9
|
+
Keywords: security,sandbox,policy,agent,audit
|
|
10
|
+
License: MIT
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
13
|
+
Project-URL: Changelog, https://github.com/XuebinMa/agent-guard/blob/main/CHANGELOG.md
|
|
14
|
+
Project-URL: Homepage, https://github.com/XuebinMa/agent-guard
|
|
15
|
+
Project-URL: Repository, https://github.com/XuebinMa/agent-guard
|
|
16
|
+
|
|
17
|
+
# agent-guard-python
|
|
18
|
+
|
|
19
|
+
> **Python bindings for execution control at the agent side-effect boundary.**
|
|
20
|
+
|
|
21
|
+
This package provides Python bindings for `agent-guard`, giving Python hosts a pre-execution decision layer before agent tool calls turn into shell commands or other side effects.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🚀 Quick Start (Python Adapters)
|
|
26
|
+
|
|
27
|
+
> **Status**: The Python wrapper layer is a **beta adapter surface**. The clearest current proof point is still shell-first execution control, and Node remains the most mature integration path in the repository.
|
|
28
|
+
|
|
29
|
+
Integrate `agent-guard` into your existing LangChain tools with a policy gate in front of the original tool:
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from agent_guard import Guard, wrap_langchain_tool
|
|
33
|
+
|
|
34
|
+
# 1. Initialize the Guard with your security policy
|
|
35
|
+
guard = Guard.from_yaml_file("policy.yaml")
|
|
36
|
+
|
|
37
|
+
# 2. Secure your existing tools
|
|
38
|
+
bash_tool = ShellTool() # Your original tool
|
|
39
|
+
secured_tool = wrap_langchain_tool(guard, bash_tool, agent_id="researcher")
|
|
40
|
+
|
|
41
|
+
# 3. Use the tool as normal - agent-guard handles the rest!
|
|
42
|
+
secured_tool.run("ls -la")
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
OpenAI-style handler wrapping is also available:
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from agent_guard import Guard, wrap_openai_tool, AgentGuardDeniedError
|
|
49
|
+
|
|
50
|
+
guard = Guard.from_yaml_file("policy.yaml")
|
|
51
|
+
|
|
52
|
+
guarded_handler = wrap_openai_tool(
|
|
53
|
+
guard,
|
|
54
|
+
lambda input_data: {"ok": True, "query": input_data["query"]},
|
|
55
|
+
tool="web_search",
|
|
56
|
+
mode="check",
|
|
57
|
+
trust_level="trusted",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
print(guarded_handler({"query": "agent-guard"}))
|
|
62
|
+
except AgentGuardDeniedError as error:
|
|
63
|
+
print("blocked", error.code)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## ✨ Features
|
|
69
|
+
|
|
70
|
+
- 🛡️ **Pre-execution policy decisions**: Put allow/deny/ask checks in front of Python tool handlers.
|
|
71
|
+
- 💻 **Shell-first execution control**: The strongest current execution path is still shell / Bash style tooling.
|
|
72
|
+
- ⚠️ **Typed adapter errors**: Distinct deny, ask-required, and execution failure exceptions.
|
|
73
|
+
- 📜 **Signed receipts**: Optional cryptographic proof of execution when you need deeper verification.
|
|
74
|
+
- 🔏 **Signed policy loading**: Optional detached-signature verification for `policy.yaml`.
|
|
75
|
+
- 📊 **Auditing support**: JSONL logs and metrics integration for operator-visible outcomes.
|
|
76
|
+
|
|
77
|
+
Current boundary note:
|
|
78
|
+
|
|
79
|
+
- non-shell tools are most often a `check`-style policy gate first
|
|
80
|
+
- shell-style execution remains the clearest current enforcement proof point
|
|
81
|
+
- Python is an active adapter surface, but still below the current Node path in maturity
|
|
82
|
+
- `Guard.execute()` / `Guard.run()` use the SDK default sandbox selection, or accept an explicit `backend=` keyword (`"none"`, `"linux-seccomp"`, `"linux-landlock"`, `"macos-seatbelt"`, `"windows-job-object"`, `"windows-appcontainer"`); a backend that is not compiled into the module or not functional on the host resolves truthfully to `"none"`, and an unknown name raises
|
|
83
|
+
- to get real isolation through `backend=`, build the module with the matching feature forwarded, e.g. `maturin develop --features extension-module,seccomp` (requires libseccomp on Linux); the default build carries no sandbox feature
|
|
84
|
+
- if the default sandbox diagnosis falls back to `NoopSandbox`, the policy gate still runs, but OS-level isolation is not equivalent
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 🔧 Installation
|
|
89
|
+
|
|
90
|
+
Version `0.2.0` is not on PyPI yet. Install it from a repository checkout
|
|
91
|
+
(requires Python and a Rust toolchain):
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
python -m pip install ./crates/agent-guard-python
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
After the synchronized release, the distribution name will be
|
|
98
|
+
`agent-guard-python`; the import name remains `agent_guard`:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from agent_guard import Guard
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
For local development in this repository:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
cd crates/agent-guard-python
|
|
108
|
+
maturin develop --features extension-module
|
|
109
|
+
pytest tests -v
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 📺 Demos
|
|
115
|
+
|
|
116
|
+
Check the `examples/` directory for full usage scenarios:
|
|
117
|
+
- `demo_langchain.py`: Comprehensive 3-line integration demo.
|
|
118
|
+
- `provenance_receipt.py`: Cryptographic verification example.
|
|
119
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
agent_guard/__init__.py,sha256=7grpLwVmtmgCAd0rc7rwSga9WPBo7kz8baPwG5a01yw,965
|
|
2
|
+
agent_guard/_agent_guard.pyd,sha256=omYKPLudWoDAUgrItd-UasbZC2l4tv3t2yDG_uzOvYo,10195456
|
|
3
|
+
agent_guard/adapters.py,sha256=OHPzHlIVNmQJI0mz5jNqfKar6-vjeU1HRHkxToRWPw4,16270
|
|
4
|
+
agent_guard/langchain.py,sha256=Nc_E8AxG0Or_1xwq45h_kWZknodYeZlZcTGg4QOcVS0,5888
|
|
5
|
+
agent_guard/openai.py,sha256=X6tpxWwSaVIPfb9dMMpFCcG0osieLvoH0in2a7nurAo,3054
|
|
6
|
+
agent_guard_python-0.2.1.dist-info/METADATA,sha256=7Dpq2h0pfjQJWCk6ETX7I9Lqn3JthEJe-NVffOk5VL4,4769
|
|
7
|
+
agent_guard_python-0.2.1.dist-info/WHEEL,sha256=7HL-TGvq44Ofp0yhZL-uRIIFHcJTIqqkRZVQ2wZidzM,96
|
|
8
|
+
agent_guard_python-0.2.1.dist-info/sboms/agent-guard-python.cyclonedx.json,sha256=nhrbDSGl2L6nGrPCNS0RR_KJ7BB8II0HvZUWliD6Nqs,245381
|
|
9
|
+
agent_guard_python-0.2.1.dist-info/RECORD,,
|