agent-hooks-sdk 0.1.0a1__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.
- agent_hooks_sdk-0.1.0a1/PKG-INFO +79 -0
- agent_hooks_sdk-0.1.0a1/README.md +49 -0
- agent_hooks_sdk-0.1.0a1/pyproject.toml +59 -0
- agent_hooks_sdk-0.1.0a1/python/Cargo.lock +332 -0
- agent_hooks_sdk-0.1.0a1/python/Cargo.toml +19 -0
- agent_hooks_sdk-0.1.0a1/python/README.md +49 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/__init__.py +66 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/_core.pyi +26 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/_types.py +279 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/approval.py +54 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/canonical.py +32 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/context.py +168 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/ctk/__init__.py +23 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/ctk/_schemas.py +36 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/ctk/harness.py +127 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/ctk/plugin.py +63 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/ctk/reference.py +139 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/ctk/runner.py +119 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/ctk/scripted.py +73 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/emitter.py +224 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/exceptions.py +41 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/interceptor.py +21 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/path.py +90 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/py.typed +0 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/schema/agent-context/agent_shutdown.schema.json +391 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/schema/agent-context/agent_startup.schema.json +391 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/schema/agent-context/input.schema.json +391 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/schema/agent-context/output.schema.json +391 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/schema/agent-context/post_model_call.schema.json +393 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/schema/agent-context/post_tool_call.schema.json +393 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/schema/agent-context/pre_model_call.schema.json +392 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/schema/agent-context/pre_tool_call.schema.json +391 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/schema/agent-context.schema.json +230 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/schema/approval.schema.json +59 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/schema/interception-point.schema.json +17 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/schema/interception-record.schema.json +56 -0
- agent_hooks_sdk-0.1.0a1/python/agent_hooks/schema/verdict.schema.json +73 -0
- agent_hooks_sdk-0.1.0a1/python/src/lib.rs +141 -0
- agent_hooks_sdk-0.1.0a1/python/tests/__init__.py +0 -0
- agent_hooks_sdk-0.1.0a1/python/tests/test_canonical.py +55 -0
- agent_hooks_sdk-0.1.0a1/python/tests/test_ctk_reference.py +28 -0
- agent_hooks_sdk-0.1.0a1/python/tests/test_decided_by.py +49 -0
- agent_hooks_sdk-0.1.0a1/python/tests/test_golden_identity.py +38 -0
- agent_hooks_sdk-0.1.0a1/python/tests/test_path.py +50 -0
- agent_hooks_sdk-0.1.0a1/python/tests/test_types.py +67 -0
- agent_hooks_sdk-0.1.0a1/rust/Cargo.toml +17 -0
- agent_hooks_sdk-0.1.0a1/rust/core/Cargo.toml +31 -0
- agent_hooks_sdk-0.1.0a1/rust/core/src/builder.rs +216 -0
- agent_hooks_sdk-0.1.0a1/rust/core/src/canonical.rs +148 -0
- agent_hooks_sdk-0.1.0a1/rust/core/src/ctk.rs +412 -0
- agent_hooks_sdk-0.1.0a1/rust/core/src/ctk_engine.rs +450 -0
- agent_hooks_sdk-0.1.0a1/rust/core/src/emitter.rs +236 -0
- agent_hooks_sdk-0.1.0a1/rust/core/src/enforce.rs +216 -0
- agent_hooks_sdk-0.1.0a1/rust/core/src/ffi_surface.rs +183 -0
- agent_hooks_sdk-0.1.0a1/rust/core/src/lib.rs +47 -0
- agent_hooks_sdk-0.1.0a1/rust/core/src/path.rs +224 -0
- agent_hooks_sdk-0.1.0a1/rust/core/src/types.rs +307 -0
- agent_hooks_sdk-0.1.0a1/rust/core/src/verdict.rs +197 -0
- agent_hooks_sdk-0.1.0a1/rust/core/tests/ctk_reference.rs +23 -0
- agent_hooks_sdk-0.1.0a1/rust/core/tests/golden_identity.rs +56 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agent-hooks-sdk
|
|
3
|
+
Version: 0.1.0a1
|
|
4
|
+
Classifier: Development Status :: 3 - Alpha
|
|
5
|
+
Classifier: Intended Audience :: Developers
|
|
6
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
11
|
+
Classifier: Programming Language :: Rust
|
|
12
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
13
|
+
Requires-Dist: jsonschema>=4.21 ; extra == 'ctk'
|
|
14
|
+
Requires-Dist: pytest>=7.4 ; extra == 'ctk'
|
|
15
|
+
Requires-Dist: agent-hooks-sdk[ctk] ; extra == 'dev'
|
|
16
|
+
Requires-Dist: mypy>=1.8 ; extra == 'dev'
|
|
17
|
+
Requires-Dist: ruff>=0.4 ; extra == 'dev'
|
|
18
|
+
Requires-Dist: maturin>=1.7,<2.0 ; extra == 'dev'
|
|
19
|
+
Provides-Extra: ctk
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Summary: Framework-neutral agent control contract: interception points, agent context, verdict, and conformance test kit. Python wrapper over the canonical Rust core.
|
|
22
|
+
Keywords: ai-agents,governance,interception,control,conformance
|
|
23
|
+
Author-email: Responsible AI <responsibleai@microsoft.com>
|
|
24
|
+
License: MIT
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
27
|
+
Project-URL: Homepage, https://github.com/responsibleai/agent-hooks
|
|
28
|
+
Project-URL: Specification, https://github.com/responsibleai/agent-hooks/blob/main/spec/AGENT-HOOKS-0.1.md
|
|
29
|
+
|
|
30
|
+
# agent-hooks (Python SDK)
|
|
31
|
+
|
|
32
|
+
Python implementation of [AGENT-HOOKS-0.1](../../spec/AGENT-HOOKS-0.1.md):
|
|
33
|
+
interception-point enums, `AgentContext` builder, `Verdict` types, host-side
|
|
34
|
+
`InterceptionEmitter`, and the Conformance Test Kit.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Not yet on PyPI (the name "agent-hooks" is held by an unrelated
|
|
38
|
+
# project; the distribution is named agent-hooks-sdk). Install from source:
|
|
39
|
+
pip install "agent-hooks-sdk @ git+https://github.com/responsibleai/agent-hooks#subdirectory=sdk/python"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Host (framework adapter) usage
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from agent_hooks import AgentContextBuilder, InterceptionEmitter, InterceptionBlocked
|
|
46
|
+
|
|
47
|
+
builder = AgentContextBuilder(agent_id="...", framework="my-fw", session_id="...")
|
|
48
|
+
emitter = InterceptionEmitter().register(my_consumer)
|
|
49
|
+
|
|
50
|
+
await emitter.emit(builder.agent_startup(tools_registered=[...]))
|
|
51
|
+
ctx = builder.pre_tool_call(call_id="tc-1", name="http_get", args={"url": u})
|
|
52
|
+
try:
|
|
53
|
+
await emitter.emit(ctx)
|
|
54
|
+
except InterceptionBlocked as e:
|
|
55
|
+
return tool_error(e.result.verdict.reason)
|
|
56
|
+
result = invoke_tool(ctx["tool_call"]["args"]) # post-transform args
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Interceptor usage
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from agent_hooks import Interceptor, AgentContext, Verdict, Decision
|
|
63
|
+
|
|
64
|
+
class MyPolicy:
|
|
65
|
+
def intercept(self, ctx: AgentContext) -> Verdict:
|
|
66
|
+
if ctx["interception_point"] == "pre_tool_call" and ctx["tool_call"]["name"] == "rm":
|
|
67
|
+
return Verdict(Decision.DENY, reason="dangerous")
|
|
68
|
+
return Verdict.ALLOW
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Running the CTK against your framework
|
|
72
|
+
|
|
73
|
+
Implement `agent_hooks.ctk.Harness` (see `conformance/HARNESS.md`), then:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pytest --agent-hooks-harness=my_pkg:MyHarness \
|
|
77
|
+
--agent-hooks-vectors=path/to/conformance/vectors
|
|
78
|
+
```
|
|
79
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# agent-hooks (Python SDK)
|
|
2
|
+
|
|
3
|
+
Python implementation of [AGENT-HOOKS-0.1](../../spec/AGENT-HOOKS-0.1.md):
|
|
4
|
+
interception-point enums, `AgentContext` builder, `Verdict` types, host-side
|
|
5
|
+
`InterceptionEmitter`, and the Conformance Test Kit.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Not yet on PyPI (the name "agent-hooks" is held by an unrelated
|
|
9
|
+
# project; the distribution is named agent-hooks-sdk). Install from source:
|
|
10
|
+
pip install "agent-hooks-sdk @ git+https://github.com/responsibleai/agent-hooks#subdirectory=sdk/python"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Host (framework adapter) usage
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from agent_hooks import AgentContextBuilder, InterceptionEmitter, InterceptionBlocked
|
|
17
|
+
|
|
18
|
+
builder = AgentContextBuilder(agent_id="...", framework="my-fw", session_id="...")
|
|
19
|
+
emitter = InterceptionEmitter().register(my_consumer)
|
|
20
|
+
|
|
21
|
+
await emitter.emit(builder.agent_startup(tools_registered=[...]))
|
|
22
|
+
ctx = builder.pre_tool_call(call_id="tc-1", name="http_get", args={"url": u})
|
|
23
|
+
try:
|
|
24
|
+
await emitter.emit(ctx)
|
|
25
|
+
except InterceptionBlocked as e:
|
|
26
|
+
return tool_error(e.result.verdict.reason)
|
|
27
|
+
result = invoke_tool(ctx["tool_call"]["args"]) # post-transform args
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Interceptor usage
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from agent_hooks import Interceptor, AgentContext, Verdict, Decision
|
|
34
|
+
|
|
35
|
+
class MyPolicy:
|
|
36
|
+
def intercept(self, ctx: AgentContext) -> Verdict:
|
|
37
|
+
if ctx["interception_point"] == "pre_tool_call" and ctx["tool_call"]["name"] == "rm":
|
|
38
|
+
return Verdict(Decision.DENY, reason="dangerous")
|
|
39
|
+
return Verdict.ALLOW
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Running the CTK against your framework
|
|
43
|
+
|
|
44
|
+
Implement `agent_hooks.ctk.Harness` (see `conformance/HARNESS.md`), then:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pytest --agent-hooks-harness=my_pkg:MyHarness \
|
|
48
|
+
--agent-hooks-vectors=path/to/conformance/vectors
|
|
49
|
+
```
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["maturin>=1.7,<2.0"]
|
|
3
|
+
build-backend = "maturin"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
# Distribution name: "agent-hooks" is squatted on PyPI and the
|
|
7
|
+
# similarity check blocks all separator variants (incl. agenthooks).
|
|
8
|
+
# Import name stays agent_hooks.
|
|
9
|
+
name = "agent-hooks-sdk"
|
|
10
|
+
version = "0.1.0a1"
|
|
11
|
+
description = "Framework-neutral agent control contract: interception points, agent context, verdict, and conformance test kit. Python wrapper over the canonical Rust core."
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.10"
|
|
14
|
+
license = { text = "MIT" }
|
|
15
|
+
authors = [{ name = "Responsible AI", email = "responsibleai@microsoft.com" }]
|
|
16
|
+
keywords = ["ai-agents", "governance", "interception", "control", "conformance"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 3 - Alpha",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Programming Language :: Rust",
|
|
26
|
+
"Topic :: Software Development :: Libraries",
|
|
27
|
+
]
|
|
28
|
+
dependencies = []
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
ctk = ["jsonschema>=4.21", "pytest>=7.4"]
|
|
32
|
+
dev = ["agent-hooks-sdk[ctk]", "mypy>=1.8", "ruff>=0.4", "maturin>=1.7,<2.0"]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://github.com/responsibleai/agent-hooks"
|
|
36
|
+
Specification = "https://github.com/responsibleai/agent-hooks/blob/main/spec/AGENT-HOOKS-0.1.md"
|
|
37
|
+
|
|
38
|
+
[project.entry-points.pytest11]
|
|
39
|
+
agent_hooks_ctk = "agent_hooks.ctk.plugin"
|
|
40
|
+
|
|
41
|
+
[tool.maturin]
|
|
42
|
+
module-name = "agent_hooks._core"
|
|
43
|
+
features = ["pyo3/extension-module"]
|
|
44
|
+
manifest-path = "python/Cargo.toml"
|
|
45
|
+
python-source = "python"
|
|
46
|
+
|
|
47
|
+
[tool.ruff]
|
|
48
|
+
line-length = 100
|
|
49
|
+
target-version = "py310"
|
|
50
|
+
|
|
51
|
+
[tool.ruff.lint]
|
|
52
|
+
select = ["E", "F", "W", "I", "B", "UP", "C4", "SIM", "RUF", "BLE"]
|
|
53
|
+
ignore = [
|
|
54
|
+
"RUF002", # en-dash in docstrings is intentional (spec section ranges)
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
[tool.mypy]
|
|
58
|
+
strict = true
|
|
59
|
+
warn_unreachable = true
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 4
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "agent-hooks-python"
|
|
7
|
+
version = "0.1.0-alpha.1"
|
|
8
|
+
dependencies = [
|
|
9
|
+
"agent-hooks-sdk",
|
|
10
|
+
"pyo3",
|
|
11
|
+
"pyo3-build-config",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[[package]]
|
|
15
|
+
name = "agent-hooks-sdk"
|
|
16
|
+
version = "0.1.0-alpha.1"
|
|
17
|
+
dependencies = [
|
|
18
|
+
"async-trait",
|
|
19
|
+
"serde",
|
|
20
|
+
"serde_jcs",
|
|
21
|
+
"serde_json",
|
|
22
|
+
"sha2",
|
|
23
|
+
"thiserror",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[[package]]
|
|
27
|
+
name = "async-trait"
|
|
28
|
+
version = "0.1.89"
|
|
29
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
30
|
+
checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
|
|
31
|
+
dependencies = [
|
|
32
|
+
"proc-macro2",
|
|
33
|
+
"quote",
|
|
34
|
+
"syn",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[[package]]
|
|
38
|
+
name = "block-buffer"
|
|
39
|
+
version = "0.10.4"
|
|
40
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
41
|
+
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
|
42
|
+
dependencies = [
|
|
43
|
+
"generic-array",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[[package]]
|
|
47
|
+
name = "cfg-if"
|
|
48
|
+
version = "1.0.4"
|
|
49
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
50
|
+
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
|
51
|
+
|
|
52
|
+
[[package]]
|
|
53
|
+
name = "cpufeatures"
|
|
54
|
+
version = "0.2.17"
|
|
55
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
56
|
+
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
|
57
|
+
dependencies = [
|
|
58
|
+
"libc",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
[[package]]
|
|
62
|
+
name = "crypto-common"
|
|
63
|
+
version = "0.1.7"
|
|
64
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
65
|
+
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
|
66
|
+
dependencies = [
|
|
67
|
+
"generic-array",
|
|
68
|
+
"typenum",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
[[package]]
|
|
72
|
+
name = "digest"
|
|
73
|
+
version = "0.10.7"
|
|
74
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
75
|
+
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
|
76
|
+
dependencies = [
|
|
77
|
+
"block-buffer",
|
|
78
|
+
"crypto-common",
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
[[package]]
|
|
82
|
+
name = "generic-array"
|
|
83
|
+
version = "0.14.7"
|
|
84
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
85
|
+
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
|
86
|
+
dependencies = [
|
|
87
|
+
"typenum",
|
|
88
|
+
"version_check",
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
[[package]]
|
|
92
|
+
name = "heck"
|
|
93
|
+
version = "0.5.0"
|
|
94
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
95
|
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
96
|
+
|
|
97
|
+
[[package]]
|
|
98
|
+
name = "itoa"
|
|
99
|
+
version = "1.0.18"
|
|
100
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
101
|
+
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
|
102
|
+
|
|
103
|
+
[[package]]
|
|
104
|
+
name = "libc"
|
|
105
|
+
version = "0.2.186"
|
|
106
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
107
|
+
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
|
108
|
+
|
|
109
|
+
[[package]]
|
|
110
|
+
name = "memchr"
|
|
111
|
+
version = "2.8.2"
|
|
112
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
113
|
+
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
|
|
114
|
+
|
|
115
|
+
[[package]]
|
|
116
|
+
name = "once_cell"
|
|
117
|
+
version = "1.21.4"
|
|
118
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
119
|
+
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|
120
|
+
|
|
121
|
+
[[package]]
|
|
122
|
+
name = "portable-atomic"
|
|
123
|
+
version = "1.13.1"
|
|
124
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
125
|
+
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
|
|
126
|
+
|
|
127
|
+
[[package]]
|
|
128
|
+
name = "proc-macro2"
|
|
129
|
+
version = "1.0.106"
|
|
130
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
131
|
+
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
|
132
|
+
dependencies = [
|
|
133
|
+
"unicode-ident",
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
[[package]]
|
|
137
|
+
name = "pyo3"
|
|
138
|
+
version = "0.29.0"
|
|
139
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
140
|
+
checksum = "cd274650b21d4bfc26a0a47587962c1edb425f69287324355cd040c3ea66071c"
|
|
141
|
+
dependencies = [
|
|
142
|
+
"libc",
|
|
143
|
+
"once_cell",
|
|
144
|
+
"portable-atomic",
|
|
145
|
+
"pyo3-build-config",
|
|
146
|
+
"pyo3-ffi",
|
|
147
|
+
"pyo3-macros",
|
|
148
|
+
]
|
|
149
|
+
|
|
150
|
+
[[package]]
|
|
151
|
+
name = "pyo3-build-config"
|
|
152
|
+
version = "0.29.0"
|
|
153
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
154
|
+
checksum = "c5e2a7d2f0d013342f295c048ad19237add5154a55b1c5a254c0ec93d4109078"
|
|
155
|
+
dependencies = [
|
|
156
|
+
"target-lexicon",
|
|
157
|
+
]
|
|
158
|
+
|
|
159
|
+
[[package]]
|
|
160
|
+
name = "pyo3-ffi"
|
|
161
|
+
version = "0.29.0"
|
|
162
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
163
|
+
checksum = "ca85c467da1bbc8d866eea5deff9cf29ea5f7785054a17da36e65bda9c05845b"
|
|
164
|
+
dependencies = [
|
|
165
|
+
"libc",
|
|
166
|
+
"pyo3-build-config",
|
|
167
|
+
]
|
|
168
|
+
|
|
169
|
+
[[package]]
|
|
170
|
+
name = "pyo3-macros"
|
|
171
|
+
version = "0.29.0"
|
|
172
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
173
|
+
checksum = "9ac53762fd065daa3194dd09337a38bd793a188100fd1a9304c4ab312d901771"
|
|
174
|
+
dependencies = [
|
|
175
|
+
"proc-macro2",
|
|
176
|
+
"pyo3-macros-backend",
|
|
177
|
+
"quote",
|
|
178
|
+
"syn",
|
|
179
|
+
]
|
|
180
|
+
|
|
181
|
+
[[package]]
|
|
182
|
+
name = "pyo3-macros-backend"
|
|
183
|
+
version = "0.29.0"
|
|
184
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
185
|
+
checksum = "4ca3a1557399783172dc5bf39cfca835157732532cba56b71d2292161e53b362"
|
|
186
|
+
dependencies = [
|
|
187
|
+
"heck",
|
|
188
|
+
"proc-macro2",
|
|
189
|
+
"quote",
|
|
190
|
+
"syn",
|
|
191
|
+
]
|
|
192
|
+
|
|
193
|
+
[[package]]
|
|
194
|
+
name = "quote"
|
|
195
|
+
version = "1.0.46"
|
|
196
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
197
|
+
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
|
|
198
|
+
dependencies = [
|
|
199
|
+
"proc-macro2",
|
|
200
|
+
]
|
|
201
|
+
|
|
202
|
+
[[package]]
|
|
203
|
+
name = "ryu-js"
|
|
204
|
+
version = "0.2.2"
|
|
205
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
206
|
+
checksum = "6518fc26bced4d53678a22d6e423e9d8716377def84545fe328236e3af070e7f"
|
|
207
|
+
|
|
208
|
+
[[package]]
|
|
209
|
+
name = "serde"
|
|
210
|
+
version = "1.0.228"
|
|
211
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
212
|
+
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
|
213
|
+
dependencies = [
|
|
214
|
+
"serde_core",
|
|
215
|
+
"serde_derive",
|
|
216
|
+
]
|
|
217
|
+
|
|
218
|
+
[[package]]
|
|
219
|
+
name = "serde_core"
|
|
220
|
+
version = "1.0.228"
|
|
221
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
222
|
+
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
|
223
|
+
dependencies = [
|
|
224
|
+
"serde_derive",
|
|
225
|
+
]
|
|
226
|
+
|
|
227
|
+
[[package]]
|
|
228
|
+
name = "serde_derive"
|
|
229
|
+
version = "1.0.228"
|
|
230
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
231
|
+
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
|
232
|
+
dependencies = [
|
|
233
|
+
"proc-macro2",
|
|
234
|
+
"quote",
|
|
235
|
+
"syn",
|
|
236
|
+
]
|
|
237
|
+
|
|
238
|
+
[[package]]
|
|
239
|
+
name = "serde_jcs"
|
|
240
|
+
version = "0.2.0"
|
|
241
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
242
|
+
checksum = "d3a60f3fda61525e439ef6d67422118f11e986566997d9021c56867ad814a0aa"
|
|
243
|
+
dependencies = [
|
|
244
|
+
"ryu-js",
|
|
245
|
+
"serde",
|
|
246
|
+
"serde_json",
|
|
247
|
+
]
|
|
248
|
+
|
|
249
|
+
[[package]]
|
|
250
|
+
name = "serde_json"
|
|
251
|
+
version = "1.0.150"
|
|
252
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
253
|
+
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
|
|
254
|
+
dependencies = [
|
|
255
|
+
"itoa",
|
|
256
|
+
"memchr",
|
|
257
|
+
"serde",
|
|
258
|
+
"serde_core",
|
|
259
|
+
"zmij",
|
|
260
|
+
]
|
|
261
|
+
|
|
262
|
+
[[package]]
|
|
263
|
+
name = "sha2"
|
|
264
|
+
version = "0.10.9"
|
|
265
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
266
|
+
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
|
267
|
+
dependencies = [
|
|
268
|
+
"cfg-if",
|
|
269
|
+
"cpufeatures",
|
|
270
|
+
"digest",
|
|
271
|
+
]
|
|
272
|
+
|
|
273
|
+
[[package]]
|
|
274
|
+
name = "syn"
|
|
275
|
+
version = "2.0.118"
|
|
276
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
277
|
+
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
|
278
|
+
dependencies = [
|
|
279
|
+
"proc-macro2",
|
|
280
|
+
"quote",
|
|
281
|
+
"unicode-ident",
|
|
282
|
+
]
|
|
283
|
+
|
|
284
|
+
[[package]]
|
|
285
|
+
name = "target-lexicon"
|
|
286
|
+
version = "0.13.5"
|
|
287
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
288
|
+
checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
289
|
+
|
|
290
|
+
[[package]]
|
|
291
|
+
name = "thiserror"
|
|
292
|
+
version = "1.0.69"
|
|
293
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
294
|
+
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
|
295
|
+
dependencies = [
|
|
296
|
+
"thiserror-impl",
|
|
297
|
+
]
|
|
298
|
+
|
|
299
|
+
[[package]]
|
|
300
|
+
name = "thiserror-impl"
|
|
301
|
+
version = "1.0.69"
|
|
302
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
303
|
+
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
|
304
|
+
dependencies = [
|
|
305
|
+
"proc-macro2",
|
|
306
|
+
"quote",
|
|
307
|
+
"syn",
|
|
308
|
+
]
|
|
309
|
+
|
|
310
|
+
[[package]]
|
|
311
|
+
name = "typenum"
|
|
312
|
+
version = "1.20.1"
|
|
313
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
314
|
+
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
|
|
315
|
+
|
|
316
|
+
[[package]]
|
|
317
|
+
name = "unicode-ident"
|
|
318
|
+
version = "1.0.24"
|
|
319
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
320
|
+
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
|
321
|
+
|
|
322
|
+
[[package]]
|
|
323
|
+
name = "version_check"
|
|
324
|
+
version = "0.9.5"
|
|
325
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
326
|
+
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
|
327
|
+
|
|
328
|
+
[[package]]
|
|
329
|
+
name = "zmij"
|
|
330
|
+
version = "1.0.21"
|
|
331
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
332
|
+
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "agent-hooks-python"
|
|
3
|
+
version = "0.1.0-alpha.1"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
license = "MIT"
|
|
6
|
+
description = "PyO3 bindings for the agent-hooks Rust core."
|
|
7
|
+
publish = false
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
|
|
10
|
+
[lib]
|
|
11
|
+
name = "_core"
|
|
12
|
+
crate-type = ["cdylib"]
|
|
13
|
+
|
|
14
|
+
[dependencies]
|
|
15
|
+
agent-hooks-sdk = { path = "../rust/core" }
|
|
16
|
+
pyo3 = { version = "0.29", features = ["extension-module", "abi3-py310"] }
|
|
17
|
+
|
|
18
|
+
[build-dependencies]
|
|
19
|
+
pyo3-build-config = "0.29"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# agent-hooks (Python SDK)
|
|
2
|
+
|
|
3
|
+
Python implementation of [AGENT-HOOKS-0.1](../../spec/AGENT-HOOKS-0.1.md):
|
|
4
|
+
interception-point enums, `AgentContext` builder, `Verdict` types, host-side
|
|
5
|
+
`InterceptionEmitter`, and the Conformance Test Kit.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Not yet on PyPI (the name "agent-hooks" is held by an unrelated
|
|
9
|
+
# project; the distribution is named agent-hooks-sdk). Install from source:
|
|
10
|
+
pip install "agent-hooks-sdk @ git+https://github.com/responsibleai/agent-hooks#subdirectory=sdk/python"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Host (framework adapter) usage
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from agent_hooks import AgentContextBuilder, InterceptionEmitter, InterceptionBlocked
|
|
17
|
+
|
|
18
|
+
builder = AgentContextBuilder(agent_id="...", framework="my-fw", session_id="...")
|
|
19
|
+
emitter = InterceptionEmitter().register(my_consumer)
|
|
20
|
+
|
|
21
|
+
await emitter.emit(builder.agent_startup(tools_registered=[...]))
|
|
22
|
+
ctx = builder.pre_tool_call(call_id="tc-1", name="http_get", args={"url": u})
|
|
23
|
+
try:
|
|
24
|
+
await emitter.emit(ctx)
|
|
25
|
+
except InterceptionBlocked as e:
|
|
26
|
+
return tool_error(e.result.verdict.reason)
|
|
27
|
+
result = invoke_tool(ctx["tool_call"]["args"]) # post-transform args
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Interceptor usage
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from agent_hooks import Interceptor, AgentContext, Verdict, Decision
|
|
34
|
+
|
|
35
|
+
class MyPolicy:
|
|
36
|
+
def intercept(self, ctx: AgentContext) -> Verdict:
|
|
37
|
+
if ctx["interception_point"] == "pre_tool_call" and ctx["tool_call"]["name"] == "rm":
|
|
38
|
+
return Verdict(Decision.DENY, reason="dangerous")
|
|
39
|
+
return Verdict.ALLOW
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Running the CTK against your framework
|
|
43
|
+
|
|
44
|
+
Implement `agent_hooks.ctk.Harness` (see `conformance/HARNESS.md`), then:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pytest --agent-hooks-harness=my_pkg:MyHarness \
|
|
48
|
+
--agent-hooks-vectors=path/to/conformance/vectors
|
|
49
|
+
```
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""agent-hooks: framework-neutral agent lifecycle hook contract.
|
|
4
|
+
|
|
5
|
+
Implements AGENT-HOOKS-0.1. See ``spec/AGENT-HOOKS-0.1.md`` for the normative
|
|
6
|
+
text. This package provides:
|
|
7
|
+
|
|
8
|
+
- :class:`InterceptionPoint`, :class:`Decision`, :class:`EnforcementMode` — enums (§3, §5, §8)
|
|
9
|
+
- :class:`Verdict`, :class:`Transform`, :class:`Evidence` — interceptor return (§5)
|
|
10
|
+
- :class:`AgentContext` and per-hook builders — host payload (§4)
|
|
11
|
+
- :class:`Interceptor`, :class:`ApprovalResolver` — protocols (§7, §9)
|
|
12
|
+
- :class:`InterceptionEmitter` — host-side helper that builds context, dispatches to
|
|
13
|
+
interceptors, applies the verdict, and returns a :class:`InterceptionRecord` (§6)
|
|
14
|
+
- :func:`canonical_json`, :func:`context_identity` — §10
|
|
15
|
+
- :mod:`agent_hooks.ctk` — Conformance Test Kit (§13)
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from agent_hooks._types import (
|
|
20
|
+
ALLOW,
|
|
21
|
+
SPEC_VERSION,
|
|
22
|
+
Decision,
|
|
23
|
+
EnforcementMode,
|
|
24
|
+
Evidence,
|
|
25
|
+
HostError,
|
|
26
|
+
InterceptionPoint,
|
|
27
|
+
InterceptionRecord,
|
|
28
|
+
Transform,
|
|
29
|
+
Verdict,
|
|
30
|
+
)
|
|
31
|
+
from agent_hooks.approval import (
|
|
32
|
+
ApprovalOutcome,
|
|
33
|
+
ApprovalRequest,
|
|
34
|
+
ApprovalResolution,
|
|
35
|
+
ApprovalResolver,
|
|
36
|
+
)
|
|
37
|
+
from agent_hooks.canonical import canonical_json, context_identity
|
|
38
|
+
from agent_hooks.context import AgentContext, AgentContextBuilder
|
|
39
|
+
from agent_hooks.emitter import InterceptionEmitter
|
|
40
|
+
from agent_hooks.exceptions import InterceptionBlocked, InterceptionSuspended
|
|
41
|
+
from agent_hooks.interceptor import Interceptor
|
|
42
|
+
|
|
43
|
+
__all__ = [
|
|
44
|
+
"ALLOW",
|
|
45
|
+
"SPEC_VERSION",
|
|
46
|
+
"AgentContext",
|
|
47
|
+
"AgentContextBuilder",
|
|
48
|
+
"ApprovalOutcome",
|
|
49
|
+
"ApprovalRequest",
|
|
50
|
+
"ApprovalResolution",
|
|
51
|
+
"ApprovalResolver",
|
|
52
|
+
"Decision",
|
|
53
|
+
"EnforcementMode",
|
|
54
|
+
"Evidence",
|
|
55
|
+
"HostError",
|
|
56
|
+
"InterceptionBlocked",
|
|
57
|
+
"InterceptionEmitter",
|
|
58
|
+
"InterceptionPoint",
|
|
59
|
+
"InterceptionRecord",
|
|
60
|
+
"InterceptionSuspended",
|
|
61
|
+
"Interceptor",
|
|
62
|
+
"Transform",
|
|
63
|
+
"Verdict",
|
|
64
|
+
"canonical_json",
|
|
65
|
+
"context_identity",
|
|
66
|
+
]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Type stubs for the PyO3 native module (agent_hooks._core).
|
|
2
|
+
# All functions take/return UTF-8 JSON strings.
|
|
3
|
+
|
|
4
|
+
SPEC_VERSION: str
|
|
5
|
+
|
|
6
|
+
class AgentHooksCoreError(ValueError):
|
|
7
|
+
"""Raised by every _core function on failure. ``.code`` is the §11
|
|
8
|
+
``host_error:*`` wire string."""
|
|
9
|
+
code: str
|
|
10
|
+
|
|
11
|
+
def spec_version() -> str: ...
|
|
12
|
+
def canonical_json(value_json: str, /) -> str: ...
|
|
13
|
+
def context_identity(ctx_json: str, /) -> str: ...
|
|
14
|
+
def validate_verdict(verdict_json: str, /) -> str: ...
|
|
15
|
+
def apply_transform(target_json: str, path: str, value_json: str, /) -> str: ...
|
|
16
|
+
def apply_transform_ctx(ctx_json: str, path: str, value_json: str, /) -> str: ...
|
|
17
|
+
def validate_transform_ctx(ctx_json: str, path: str, value_json: str, /) -> str: ...
|
|
18
|
+
def finalize(
|
|
19
|
+
ctx_json: str, verdict_json: str, mode: str, input_identity: str, decided_by: int, /
|
|
20
|
+
) -> str: ...
|
|
21
|
+
|
|
22
|
+
# CTK engine (§13.2)
|
|
23
|
+
def ctk_scripted_intercept(rules_json: str, ctx_json: str, /) -> str: ...
|
|
24
|
+
def ctk_scripted_resolve(rules_json: str, ctx_json: str, identity: str, /) -> str: ...
|
|
25
|
+
def ctk_should_skip(vector_json: str, harness_caps_json: str, /) -> str: ...
|
|
26
|
+
def ctk_assert(vector_json: str, recorded_json: str, run_record_json: str, /) -> str: ...
|