aevs 0.1.0__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.
- aevs-0.1.0/LICENSE +21 -0
- aevs-0.1.0/PKG-INFO +263 -0
- aevs-0.1.0/README.md +228 -0
- aevs-0.1.0/pyproject.toml +75 -0
- aevs-0.1.0/src/aevs/__init__.py +99 -0
- aevs-0.1.0/src/aevs/_api.py +658 -0
- aevs-0.1.0/src/aevs/_drainer.py +177 -0
- aevs-0.1.0/src/aevs/_version.py +6 -0
- aevs-0.1.0/src/aevs/adapters/__init__.py +0 -0
- aevs-0.1.0/src/aevs/adapters/base.py +57 -0
- aevs-0.1.0/src/aevs/adapters/langchain.py +229 -0
- aevs-0.1.0/src/aevs/adapters/mcp.py +278 -0
- aevs-0.1.0/src/aevs/config.py +170 -0
- aevs-0.1.0/src/aevs/core/__init__.py +0 -0
- aevs-0.1.0/src/aevs/core/buffer.py +329 -0
- aevs-0.1.0/src/aevs/core/client.py +278 -0
- aevs-0.1.0/src/aevs/core/receipt.py +129 -0
- aevs-0.1.0/src/aevs/core/serializer.py +103 -0
- aevs-0.1.0/src/aevs/core/signer.py +40 -0
- aevs-0.1.0/src/aevs/core/types.py +32 -0
- aevs-0.1.0/src/aevs/crypto/__init__.py +0 -0
- aevs-0.1.0/src/aevs/crypto/chain.py +39 -0
- aevs-0.1.0/src/aevs/crypto/hkdf.py +16 -0
- aevs-0.1.0/src/aevs/crypto/hmac_auth.py +12 -0
- aevs-0.1.0/src/aevs/exceptions.py +18 -0
- aevs-0.1.0/src/aevs/py.typed +0 -0
aevs-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Fetch.AI
|
|
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.
|
aevs-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: aevs
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Agent Execution Verification System — transparent audit SDK for AI agents
|
|
5
|
+
Home-page: https://github.com/fetchai/AEVS-sdk
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: ai,agents,verification,audit,langchain,mcp
|
|
8
|
+
Author: Devendra Chauhan
|
|
9
|
+
Author-email: devendra.chauhan@fetch.ai
|
|
10
|
+
Requires-Python: >=3.10,<4.0
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Security
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Provides-Extra: langchain
|
|
23
|
+
Provides-Extra: mcp
|
|
24
|
+
Requires-Dist: cryptography (>=43.0)
|
|
25
|
+
Requires-Dist: httpx (>=0.27)
|
|
26
|
+
Requires-Dist: langchain-core (>=0.2) ; extra == "langchain"
|
|
27
|
+
Requires-Dist: mcp (>=1.20) ; extra == "mcp"
|
|
28
|
+
Project-URL: Bug Tracker, https://github.com/fetchai/AEVS-sdk/issues
|
|
29
|
+
Project-URL: Changelog, https://github.com/fetchai/AEVS-sdk/blob/main/CHANGELOG.md
|
|
30
|
+
Project-URL: Documentation, https://github.com/fetchai/AEVS-sdk#readme
|
|
31
|
+
Project-URL: Repository, https://github.com/fetchai/AEVS-sdk
|
|
32
|
+
Project-URL: Source, https://github.com/fetchai/AEVS-sdk
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# AEVS SDK
|
|
36
|
+
|
|
37
|
+
Agent Execution Verification System — transparent audit SDK for AI agents.
|
|
38
|
+
|
|
39
|
+
Intercepts tool calls from supported frameworks, builds tamper-evident receipts (HMAC-signed, hash-chained), and sends them to the AEVS backend. Zero changes to your agent code.
|
|
40
|
+
|
|
41
|
+
## Compatibility
|
|
42
|
+
|
|
43
|
+
- Python 3.10+
|
|
44
|
+
- Framework adapters:
|
|
45
|
+
- `aevs[langchain]` for LangChain / LangGraph tool interception
|
|
46
|
+
- `aevs[mcp]` for MCP tool interception (requires `mcp>=1.20`)
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install aevs
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
With framework extras:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install aevs[langchain] # LangChain / LangGraph support
|
|
58
|
+
pip install aevs[mcp] # MCP tool support
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Quick Start
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
import aevs
|
|
65
|
+
|
|
66
|
+
aevs.configure(api_key="aevs_sk_<key_id>_<hex_secret>")
|
|
67
|
+
aevs.enable()
|
|
68
|
+
|
|
69
|
+
# Every tool call from this point is intercepted.
|
|
70
|
+
# No changes to tools, agents, or LLM setup.
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## API
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
aevs.configure(api_key=..., **options) # Set configuration (required before enable)
|
|
77
|
+
aevs.enable() # Auto-detect frameworks and start intercepting
|
|
78
|
+
aevs.enable(frameworks=["langchain"]) # Or specify explicitly
|
|
79
|
+
aevs.disable() # Unpatch all frameworks, restore originals
|
|
80
|
+
aevs.flush() # Force-send buffered receipts to backend
|
|
81
|
+
aevs.get_session_id() # UUID minted at enable(); stamped on every receipt
|
|
82
|
+
aevs.is_healthy() # False after sustained buffer write failures
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Configuration Options
|
|
86
|
+
|
|
87
|
+
| Parameter | Default | Description |
|
|
88
|
+
|-----------|---------|-------------|
|
|
89
|
+
| `api_key` | *(required)* | SDK key from customer creation (`aevs_sk_<id>_<hex>`) |
|
|
90
|
+
| `agent_id` | `None` | Agent UUID to tag receipts with |
|
|
91
|
+
| `base_url` | `https://api.aevs.fetch.ai/v1` | AEVS backend URL |
|
|
92
|
+
| `signing_timeout_ms` | `2000` | HTTP timeout for receipt submission |
|
|
93
|
+
| `float_handling` | `"decimal_string"` | How floats are serialized (`decimal_string` or `raise`) |
|
|
94
|
+
| `float_precision` | `6` | Decimal places for float serialization |
|
|
95
|
+
| `max_payload_bytes` | `1048576` | Max receipt payload size (1 MB) |
|
|
96
|
+
| `buffer_path` | `~/.aevs/buffer.db` | SQLite buffer file path |
|
|
97
|
+
| `max_buffer_records` | `10000` | Max buffered receipts before eviction |
|
|
98
|
+
| `drain_interval_ms` | `5000` | Background flush interval |
|
|
99
|
+
| `max_reference_entries` | `1000` | Reference ID registry capacity |
|
|
100
|
+
|
|
101
|
+
### Reference IDs
|
|
102
|
+
|
|
103
|
+
Every intercepted tool call gets a `reference_id` (UUID v4) embedded in its receipt. The SDK keeps these in a bounded FIFO registry.
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
response = await agent.ainvoke({"messages": [("user", query)]})
|
|
107
|
+
refs = aevs.get_reference_ids(clear=True)
|
|
108
|
+
# [{"seq": 1, "tool_name": "search", "reference_id": "abc-...", "run_id": "def-..."}, ...]
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Verify any `reference_id` via the public backend endpoint (no auth required):
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
GET /v1/receipts/verify/{reference_id}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Reference ID Registry
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
aevs.get_reference_id(run_id) # Lookup by framework run_id
|
|
121
|
+
aevs.get_reference_ids(clear=True) # Get all entries, then clear
|
|
122
|
+
aevs.clear_reference_ids() # Drop all entries
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Session IDs
|
|
126
|
+
|
|
127
|
+
Each `enable()` mints a fresh UUIDv4 **session id**. The id is stamped
|
|
128
|
+
on every receipt produced in that session and participates in the hash
|
|
129
|
+
chain anchor — two SDK processes that share an API key cannot fork the
|
|
130
|
+
chain by construction.
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
aevs.enable()
|
|
134
|
+
session = aevs.get_session_id()
|
|
135
|
+
# "5db7d195-f84c-4f90-ae12-d74d001d3f9d"
|
|
136
|
+
|
|
137
|
+
aevs.disable()
|
|
138
|
+
aevs.get_session_id()
|
|
139
|
+
# None
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Useful for log correlation: every receipt carries `session_id`, so
|
|
143
|
+
filtering receipts by session in the AEVS backend isolates a single
|
|
144
|
+
SDK run.
|
|
145
|
+
|
|
146
|
+
## Data & privacy
|
|
147
|
+
|
|
148
|
+
AEVS receipts may include **tool inputs and outputs**, which can contain secrets or PII depending
|
|
149
|
+
on what your tools return (e.g. prompts, retrieved documents, API responses).
|
|
150
|
+
|
|
151
|
+
- Receipts are buffered locally in an encrypted SQLite database (default `~/.aevs/buffer.db`).
|
|
152
|
+
- Receipts are submitted to the AEVS backend over HTTPS by default (`base_url`).
|
|
153
|
+
|
|
154
|
+
You are responsible for ensuring your tool layer does not emit sensitive data you cannot store or
|
|
155
|
+
transmit. If needed, redact at the tool boundary (before data reaches the agent runtime).
|
|
156
|
+
|
|
157
|
+
## Threat model / non-goals
|
|
158
|
+
|
|
159
|
+
- AEVS is **tamper-evident**, not tamper-proof. It helps detect modification/reordering of receipts
|
|
160
|
+
after the fact; it does not secure a fully compromised host process.
|
|
161
|
+
- The SDK signs requests with a key derived from your API key secret; protect the API key like any
|
|
162
|
+
other credential.
|
|
163
|
+
|
|
164
|
+
## Development
|
|
165
|
+
|
|
166
|
+
### Prerequisites
|
|
167
|
+
|
|
168
|
+
- Python 3.10+
|
|
169
|
+
- [Poetry](https://python-poetry.org/)
|
|
170
|
+
|
|
171
|
+
### Setup
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
git clone https://github.com/fetchai/AEVS-sdk.git && cd AEVS-sdk
|
|
175
|
+
make install # poetry install --all-extras
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Common commands
|
|
179
|
+
|
|
180
|
+
The `Makefile` wraps the everyday workflow. Run `make help` to see the
|
|
181
|
+
full list.
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
make test # run the test suite
|
|
185
|
+
make test-cov # tests with coverage (HTML report in ./htmlcov)
|
|
186
|
+
make lint # ruff check
|
|
187
|
+
make format # ruff format + auto-fix
|
|
188
|
+
make typecheck # mypy --strict on src/
|
|
189
|
+
make check # lint + typecheck + tests (the CI gate)
|
|
190
|
+
make build # build sdist + wheel into ./dist
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
You can still call the underlying tools directly:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
poetry run pytest tests/test_integration.py -v
|
|
197
|
+
poetry run ruff check src/ tests/
|
|
198
|
+
poetry run mypy src/
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Contributing
|
|
202
|
+
|
|
203
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the full guide — branch
|
|
204
|
+
naming, Conventional Commits, the PR checklist, and release flow.
|
|
205
|
+
|
|
206
|
+
## Reporting Security Issues
|
|
207
|
+
|
|
208
|
+
Please **do not** open a public GitHub issue for security problems.
|
|
209
|
+
See [SECURITY.md](SECURITY.md) for the disclosure process.
|
|
210
|
+
|
|
211
|
+
## Architecture
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
Agent (LangChain / MCP)
|
|
215
|
+
│
|
|
216
|
+
▼ tool call intercepted
|
|
217
|
+
ReceiptBuilder ──▶ HMAC sign + hash chain
|
|
218
|
+
│
|
|
219
|
+
▼
|
|
220
|
+
LocalBuffer (SQLite, encrypted at rest)
|
|
221
|
+
│
|
|
222
|
+
▼ background drainer
|
|
223
|
+
AEVSClient ──▶ POST /v1/receipts ──▶ AEVS Backend
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
- **Interception**: Framework-specific patches capture tool inputs/outputs
|
|
227
|
+
- **Signing**: Each receipt is HMAC-signed (HKDF-derived keys) with a hash chain linking sequential calls
|
|
228
|
+
- **Buffering**: Receipts are encrypted and stored locally in SQLite, flushed in the background
|
|
229
|
+
- **Resilience**: Buffer survives process restarts; flush retries on transient failures
|
|
230
|
+
|
|
231
|
+
## Project Structure
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
aevs-sdk/
|
|
235
|
+
├── src/aevs/
|
|
236
|
+
│ ├── __init__.py Public API (configure, enable, disable, flush)
|
|
237
|
+
│ ├── _api.py Core state management, enable/disable, flush
|
|
238
|
+
│ ├── _drainer.py Background flush of buffered receipts
|
|
239
|
+
│ ├── _version.py Package version
|
|
240
|
+
│ ├── config.py Configuration dataclass + validation
|
|
241
|
+
│ ├── exceptions.py SDK exception types
|
|
242
|
+
│ ├── adapters/ Framework-specific interceptors
|
|
243
|
+
│ │ ├── base.py Base adapter interface
|
|
244
|
+
│ │ ├── langchain.py LangChain / LangGraph interceptor
|
|
245
|
+
│ │ └── mcp.py MCP tool interceptor
|
|
246
|
+
│ ├── core/
|
|
247
|
+
│ │ ├── buffer.py Encrypted SQLite local buffer
|
|
248
|
+
│ │ ├── client.py HTTP client (sync + async)
|
|
249
|
+
│ │ ├── receipt.py ReceiptBuilder — HMAC, hash chain
|
|
250
|
+
│ │ ├── serializer.py Canonical JSON serialization
|
|
251
|
+
│ │ ├── signer.py Request signing (HKDF + HMAC)
|
|
252
|
+
│ │ └── types.py Typed payload shapes
|
|
253
|
+
│ └── crypto/
|
|
254
|
+
│ ├── chain.py Hash chain helpers
|
|
255
|
+
│ ├── hkdf.py HKDF key derivation
|
|
256
|
+
│ └── hmac_auth.py HMAC authentication
|
|
257
|
+
├── tests/ Test suite
|
|
258
|
+
├── pyproject.toml Poetry packaging + tool config
|
|
259
|
+
├── poetry.lock
|
|
260
|
+
├── LICENSE
|
|
261
|
+
└── README.md
|
|
262
|
+
```
|
|
263
|
+
|
aevs-0.1.0/README.md
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# AEVS SDK
|
|
2
|
+
|
|
3
|
+
Agent Execution Verification System — transparent audit SDK for AI agents.
|
|
4
|
+
|
|
5
|
+
Intercepts tool calls from supported frameworks, builds tamper-evident receipts (HMAC-signed, hash-chained), and sends them to the AEVS backend. Zero changes to your agent code.
|
|
6
|
+
|
|
7
|
+
## Compatibility
|
|
8
|
+
|
|
9
|
+
- Python 3.10+
|
|
10
|
+
- Framework adapters:
|
|
11
|
+
- `aevs[langchain]` for LangChain / LangGraph tool interception
|
|
12
|
+
- `aevs[mcp]` for MCP tool interception (requires `mcp>=1.20`)
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install aevs
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
With framework extras:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install aevs[langchain] # LangChain / LangGraph support
|
|
24
|
+
pip install aevs[mcp] # MCP tool support
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
import aevs
|
|
31
|
+
|
|
32
|
+
aevs.configure(api_key="aevs_sk_<key_id>_<hex_secret>")
|
|
33
|
+
aevs.enable()
|
|
34
|
+
|
|
35
|
+
# Every tool call from this point is intercepted.
|
|
36
|
+
# No changes to tools, agents, or LLM setup.
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## API
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
aevs.configure(api_key=..., **options) # Set configuration (required before enable)
|
|
43
|
+
aevs.enable() # Auto-detect frameworks and start intercepting
|
|
44
|
+
aevs.enable(frameworks=["langchain"]) # Or specify explicitly
|
|
45
|
+
aevs.disable() # Unpatch all frameworks, restore originals
|
|
46
|
+
aevs.flush() # Force-send buffered receipts to backend
|
|
47
|
+
aevs.get_session_id() # UUID minted at enable(); stamped on every receipt
|
|
48
|
+
aevs.is_healthy() # False after sustained buffer write failures
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Configuration Options
|
|
52
|
+
|
|
53
|
+
| Parameter | Default | Description |
|
|
54
|
+
|-----------|---------|-------------|
|
|
55
|
+
| `api_key` | *(required)* | SDK key from customer creation (`aevs_sk_<id>_<hex>`) |
|
|
56
|
+
| `agent_id` | `None` | Agent UUID to tag receipts with |
|
|
57
|
+
| `base_url` | `https://api.aevs.fetch.ai/v1` | AEVS backend URL |
|
|
58
|
+
| `signing_timeout_ms` | `2000` | HTTP timeout for receipt submission |
|
|
59
|
+
| `float_handling` | `"decimal_string"` | How floats are serialized (`decimal_string` or `raise`) |
|
|
60
|
+
| `float_precision` | `6` | Decimal places for float serialization |
|
|
61
|
+
| `max_payload_bytes` | `1048576` | Max receipt payload size (1 MB) |
|
|
62
|
+
| `buffer_path` | `~/.aevs/buffer.db` | SQLite buffer file path |
|
|
63
|
+
| `max_buffer_records` | `10000` | Max buffered receipts before eviction |
|
|
64
|
+
| `drain_interval_ms` | `5000` | Background flush interval |
|
|
65
|
+
| `max_reference_entries` | `1000` | Reference ID registry capacity |
|
|
66
|
+
|
|
67
|
+
### Reference IDs
|
|
68
|
+
|
|
69
|
+
Every intercepted tool call gets a `reference_id` (UUID v4) embedded in its receipt. The SDK keeps these in a bounded FIFO registry.
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
response = await agent.ainvoke({"messages": [("user", query)]})
|
|
73
|
+
refs = aevs.get_reference_ids(clear=True)
|
|
74
|
+
# [{"seq": 1, "tool_name": "search", "reference_id": "abc-...", "run_id": "def-..."}, ...]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Verify any `reference_id` via the public backend endpoint (no auth required):
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
GET /v1/receipts/verify/{reference_id}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Reference ID Registry
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
aevs.get_reference_id(run_id) # Lookup by framework run_id
|
|
87
|
+
aevs.get_reference_ids(clear=True) # Get all entries, then clear
|
|
88
|
+
aevs.clear_reference_ids() # Drop all entries
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Session IDs
|
|
92
|
+
|
|
93
|
+
Each `enable()` mints a fresh UUIDv4 **session id**. The id is stamped
|
|
94
|
+
on every receipt produced in that session and participates in the hash
|
|
95
|
+
chain anchor — two SDK processes that share an API key cannot fork the
|
|
96
|
+
chain by construction.
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
aevs.enable()
|
|
100
|
+
session = aevs.get_session_id()
|
|
101
|
+
# "5db7d195-f84c-4f90-ae12-d74d001d3f9d"
|
|
102
|
+
|
|
103
|
+
aevs.disable()
|
|
104
|
+
aevs.get_session_id()
|
|
105
|
+
# None
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Useful for log correlation: every receipt carries `session_id`, so
|
|
109
|
+
filtering receipts by session in the AEVS backend isolates a single
|
|
110
|
+
SDK run.
|
|
111
|
+
|
|
112
|
+
## Data & privacy
|
|
113
|
+
|
|
114
|
+
AEVS receipts may include **tool inputs and outputs**, which can contain secrets or PII depending
|
|
115
|
+
on what your tools return (e.g. prompts, retrieved documents, API responses).
|
|
116
|
+
|
|
117
|
+
- Receipts are buffered locally in an encrypted SQLite database (default `~/.aevs/buffer.db`).
|
|
118
|
+
- Receipts are submitted to the AEVS backend over HTTPS by default (`base_url`).
|
|
119
|
+
|
|
120
|
+
You are responsible for ensuring your tool layer does not emit sensitive data you cannot store or
|
|
121
|
+
transmit. If needed, redact at the tool boundary (before data reaches the agent runtime).
|
|
122
|
+
|
|
123
|
+
## Threat model / non-goals
|
|
124
|
+
|
|
125
|
+
- AEVS is **tamper-evident**, not tamper-proof. It helps detect modification/reordering of receipts
|
|
126
|
+
after the fact; it does not secure a fully compromised host process.
|
|
127
|
+
- The SDK signs requests with a key derived from your API key secret; protect the API key like any
|
|
128
|
+
other credential.
|
|
129
|
+
|
|
130
|
+
## Development
|
|
131
|
+
|
|
132
|
+
### Prerequisites
|
|
133
|
+
|
|
134
|
+
- Python 3.10+
|
|
135
|
+
- [Poetry](https://python-poetry.org/)
|
|
136
|
+
|
|
137
|
+
### Setup
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
git clone https://github.com/fetchai/AEVS-sdk.git && cd AEVS-sdk
|
|
141
|
+
make install # poetry install --all-extras
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Common commands
|
|
145
|
+
|
|
146
|
+
The `Makefile` wraps the everyday workflow. Run `make help` to see the
|
|
147
|
+
full list.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
make test # run the test suite
|
|
151
|
+
make test-cov # tests with coverage (HTML report in ./htmlcov)
|
|
152
|
+
make lint # ruff check
|
|
153
|
+
make format # ruff format + auto-fix
|
|
154
|
+
make typecheck # mypy --strict on src/
|
|
155
|
+
make check # lint + typecheck + tests (the CI gate)
|
|
156
|
+
make build # build sdist + wheel into ./dist
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
You can still call the underlying tools directly:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
poetry run pytest tests/test_integration.py -v
|
|
163
|
+
poetry run ruff check src/ tests/
|
|
164
|
+
poetry run mypy src/
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Contributing
|
|
168
|
+
|
|
169
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the full guide — branch
|
|
170
|
+
naming, Conventional Commits, the PR checklist, and release flow.
|
|
171
|
+
|
|
172
|
+
## Reporting Security Issues
|
|
173
|
+
|
|
174
|
+
Please **do not** open a public GitHub issue for security problems.
|
|
175
|
+
See [SECURITY.md](SECURITY.md) for the disclosure process.
|
|
176
|
+
|
|
177
|
+
## Architecture
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
Agent (LangChain / MCP)
|
|
181
|
+
│
|
|
182
|
+
▼ tool call intercepted
|
|
183
|
+
ReceiptBuilder ──▶ HMAC sign + hash chain
|
|
184
|
+
│
|
|
185
|
+
▼
|
|
186
|
+
LocalBuffer (SQLite, encrypted at rest)
|
|
187
|
+
│
|
|
188
|
+
▼ background drainer
|
|
189
|
+
AEVSClient ──▶ POST /v1/receipts ──▶ AEVS Backend
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
- **Interception**: Framework-specific patches capture tool inputs/outputs
|
|
193
|
+
- **Signing**: Each receipt is HMAC-signed (HKDF-derived keys) with a hash chain linking sequential calls
|
|
194
|
+
- **Buffering**: Receipts are encrypted and stored locally in SQLite, flushed in the background
|
|
195
|
+
- **Resilience**: Buffer survives process restarts; flush retries on transient failures
|
|
196
|
+
|
|
197
|
+
## Project Structure
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
aevs-sdk/
|
|
201
|
+
├── src/aevs/
|
|
202
|
+
│ ├── __init__.py Public API (configure, enable, disable, flush)
|
|
203
|
+
│ ├── _api.py Core state management, enable/disable, flush
|
|
204
|
+
│ ├── _drainer.py Background flush of buffered receipts
|
|
205
|
+
│ ├── _version.py Package version
|
|
206
|
+
│ ├── config.py Configuration dataclass + validation
|
|
207
|
+
│ ├── exceptions.py SDK exception types
|
|
208
|
+
│ ├── adapters/ Framework-specific interceptors
|
|
209
|
+
│ │ ├── base.py Base adapter interface
|
|
210
|
+
│ │ ├── langchain.py LangChain / LangGraph interceptor
|
|
211
|
+
│ │ └── mcp.py MCP tool interceptor
|
|
212
|
+
│ ├── core/
|
|
213
|
+
│ │ ├── buffer.py Encrypted SQLite local buffer
|
|
214
|
+
│ │ ├── client.py HTTP client (sync + async)
|
|
215
|
+
│ │ ├── receipt.py ReceiptBuilder — HMAC, hash chain
|
|
216
|
+
│ │ ├── serializer.py Canonical JSON serialization
|
|
217
|
+
│ │ ├── signer.py Request signing (HKDF + HMAC)
|
|
218
|
+
│ │ └── types.py Typed payload shapes
|
|
219
|
+
│ └── crypto/
|
|
220
|
+
│ ├── chain.py Hash chain helpers
|
|
221
|
+
│ ├── hkdf.py HKDF key derivation
|
|
222
|
+
│ └── hmac_auth.py HMAC authentication
|
|
223
|
+
├── tests/ Test suite
|
|
224
|
+
├── pyproject.toml Poetry packaging + tool config
|
|
225
|
+
├── poetry.lock
|
|
226
|
+
├── LICENSE
|
|
227
|
+
└── README.md
|
|
228
|
+
```
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "aevs"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Agent Execution Verification System — transparent audit SDK for AI agents"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
authors = [
|
|
8
|
+
"Devendra Chauhan <devendra.chauhan@fetch.ai>",
|
|
9
|
+
"Mehul Gaidhani <mehul.gaidhani@fetch.ai>",
|
|
10
|
+
]
|
|
11
|
+
homepage = "https://github.com/fetchai/AEVS-sdk"
|
|
12
|
+
repository = "https://github.com/fetchai/AEVS-sdk"
|
|
13
|
+
packages = [{include = "aevs", from = "src"}]
|
|
14
|
+
keywords = ["ai", "agents", "verification", "audit", "langchain", "mcp"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Topic :: Security",
|
|
25
|
+
"Topic :: Software Development :: Libraries",
|
|
26
|
+
"Typing :: Typed",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[tool.poetry.urls]
|
|
30
|
+
"Bug Tracker" = "https://github.com/fetchai/AEVS-sdk/issues"
|
|
31
|
+
"Changelog" = "https://github.com/fetchai/AEVS-sdk/blob/main/CHANGELOG.md"
|
|
32
|
+
"Documentation" = "https://github.com/fetchai/AEVS-sdk#readme"
|
|
33
|
+
"Source" = "https://github.com/fetchai/AEVS-sdk"
|
|
34
|
+
|
|
35
|
+
[tool.poetry.dependencies]
|
|
36
|
+
python = "^3.10"
|
|
37
|
+
httpx = ">=0.27"
|
|
38
|
+
cryptography = ">=43.0"
|
|
39
|
+
mcp = {version = ">=1.20", optional = true}
|
|
40
|
+
langchain-core = {version = ">=0.2", optional = true}
|
|
41
|
+
|
|
42
|
+
[tool.poetry.extras]
|
|
43
|
+
langchain = ["langchain-core"]
|
|
44
|
+
mcp = ["mcp"]
|
|
45
|
+
|
|
46
|
+
[tool.poetry.group.dev.dependencies]
|
|
47
|
+
pytest = ">=8.0"
|
|
48
|
+
pytest-asyncio = ">=0.23"
|
|
49
|
+
pytest-cov = ">=5.0"
|
|
50
|
+
ruff = ">=0.4"
|
|
51
|
+
mypy = ">=1.10"
|
|
52
|
+
respx = ">=0.21"
|
|
53
|
+
langchain-core = ">=0.2"
|
|
54
|
+
mcp = ">=1.20"
|
|
55
|
+
|
|
56
|
+
[build-system]
|
|
57
|
+
requires = ["poetry-core"]
|
|
58
|
+
build-backend = "poetry.core.masonry.api"
|
|
59
|
+
|
|
60
|
+
[tool.ruff]
|
|
61
|
+
target-version = "py310"
|
|
62
|
+
line-length = 100
|
|
63
|
+
|
|
64
|
+
[tool.ruff.lint]
|
|
65
|
+
select = ["E", "F", "I", "N", "W", "UP"]
|
|
66
|
+
|
|
67
|
+
[tool.pytest.ini_options]
|
|
68
|
+
testpaths = ["tests"]
|
|
69
|
+
asyncio_mode = "auto"
|
|
70
|
+
# Import `aevs` from ./src so `pytest` matches `PYTHONPATH=src` (avoids stale site-packages).
|
|
71
|
+
pythonpath = ["src"]
|
|
72
|
+
|
|
73
|
+
[tool.mypy]
|
|
74
|
+
python_version = "3.10"
|
|
75
|
+
strict = true
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from aevs._version import __version__
|
|
2
|
+
from aevs.config import AEVSConfig, configure, reset_config
|
|
3
|
+
from aevs.exceptions import (
|
|
4
|
+
AEVSAuthError,
|
|
5
|
+
AEVSBufferError,
|
|
6
|
+
AEVSConfigError,
|
|
7
|
+
AEVSError,
|
|
8
|
+
AEVSSerializationError,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"__version__",
|
|
13
|
+
"AEVSAuthError",
|
|
14
|
+
"AEVSBufferError",
|
|
15
|
+
"AEVSConfig",
|
|
16
|
+
"AEVSConfigError",
|
|
17
|
+
"AEVSError",
|
|
18
|
+
"AEVSSerializationError",
|
|
19
|
+
"clear_reference_ids",
|
|
20
|
+
"configure",
|
|
21
|
+
"disable",
|
|
22
|
+
"enable",
|
|
23
|
+
"flush",
|
|
24
|
+
"get_reference_id",
|
|
25
|
+
"get_reference_ids",
|
|
26
|
+
"get_session_id",
|
|
27
|
+
"is_healthy",
|
|
28
|
+
"reset_config",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def enable(*, frameworks: list[str] | None = None) -> None:
|
|
33
|
+
"""Detect installed frameworks and patch them to intercept tool calls."""
|
|
34
|
+
from aevs._api import enable as _enable
|
|
35
|
+
|
|
36
|
+
_enable(frameworks=frameworks)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def disable() -> None:
|
|
40
|
+
"""Unpatch all frameworks, restore original behavior."""
|
|
41
|
+
from aevs._api import disable as _disable
|
|
42
|
+
|
|
43
|
+
_disable()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def flush() -> None:
|
|
47
|
+
"""Force-send all buffered receipts to the backend."""
|
|
48
|
+
from aevs._api import flush as _flush
|
|
49
|
+
|
|
50
|
+
_flush()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def is_healthy(*, threshold: int = 3) -> bool:
|
|
54
|
+
"""Return ``False`` when the receipt buffer has had *threshold* or more
|
|
55
|
+
consecutive write failures — indicating a sustained storage problem.
|
|
56
|
+
|
|
57
|
+
Safe to call at any time; never raises.
|
|
58
|
+
"""
|
|
59
|
+
from aevs._api import is_healthy as _is_healthy
|
|
60
|
+
|
|
61
|
+
return _is_healthy(threshold=threshold)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get_reference_id(lookup_id: str) -> str | None:
|
|
65
|
+
"""Return the AEVS reference_id for a run_id or tool_call_id."""
|
|
66
|
+
from aevs._api import get_reference_id as _get
|
|
67
|
+
|
|
68
|
+
return _get(lookup_id)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def get_reference_ids(*, clear: bool = False) -> list[dict[str, str | int | None]]:
|
|
72
|
+
"""Return all reference entries recorded since the last clear.
|
|
73
|
+
|
|
74
|
+
Pass ``clear=True`` to empty the registry after reading (recommended
|
|
75
|
+
for per-request web applications).
|
|
76
|
+
"""
|
|
77
|
+
from aevs._api import get_reference_ids as _get
|
|
78
|
+
|
|
79
|
+
return _get(clear=clear)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def clear_reference_ids() -> None:
|
|
83
|
+
"""Drop all stored reference entries."""
|
|
84
|
+
from aevs._api import clear_reference_ids as _clear
|
|
85
|
+
|
|
86
|
+
_clear()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def get_session_id() -> str | None:
|
|
90
|
+
"""Return the active AEVS session UUID, or ``None`` when disabled.
|
|
91
|
+
|
|
92
|
+
A new UUIDv4 is minted on each ``enable()`` (or recovered from the
|
|
93
|
+
buffer on mid-session crash recovery). Useful for log correlation —
|
|
94
|
+
every receipt in the session carries this id and the chain anchor
|
|
95
|
+
is derived from it.
|
|
96
|
+
"""
|
|
97
|
+
from aevs._api import get_session_id as _get
|
|
98
|
+
|
|
99
|
+
return _get()
|