openadapt-verify 0.2.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.
- openadapt_verify-0.2.0/.gitignore +7 -0
- openadapt_verify-0.2.0/LICENSE +21 -0
- openadapt_verify-0.2.0/PKG-INFO +382 -0
- openadapt_verify-0.2.0/README.md +357 -0
- openadapt_verify-0.2.0/pyproject.toml +88 -0
- openadapt_verify-0.2.0/scripts/check_clean_install.py +319 -0
- openadapt_verify-0.2.0/scripts/check_distribution.py +144 -0
- openadapt_verify-0.2.0/src/openadapt_verify/__init__.py +65 -0
- openadapt_verify-0.2.0/src/openadapt_verify/__main__.py +3 -0
- openadapt_verify-0.2.0/src/openadapt_verify/_ledger_config.py +21 -0
- openadapt_verify-0.2.0/src/openadapt_verify/adapters/__init__.py +1 -0
- openadapt_verify-0.2.0/src/openadapt_verify/adapters/browser_use.py +39 -0
- openadapt_verify-0.2.0/src/openadapt_verify/adapters/claude_sdk.py +5 -0
- openadapt_verify-0.2.0/src/openadapt_verify/adapters/openai_agents.py +5 -0
- openadapt_verify-0.2.0/src/openadapt_verify/adapters/stagehand.py +32 -0
- openadapt_verify-0.2.0/src/openadapt_verify/audit.py +742 -0
- openadapt_verify-0.2.0/src/openadapt_verify/audit_demo.py +106 -0
- openadapt_verify-0.2.0/src/openadapt_verify/cli.py +463 -0
- openadapt_verify-0.2.0/src/openadapt_verify/guard.py +130 -0
- openadapt_verify-0.2.0/src/openadapt_verify/mcp.py +24 -0
- openadapt_verify-0.2.0/src/openadapt_verify/quickstart.py +567 -0
- openadapt_verify-0.2.0/src/openadapt_verify/shadow.py +5 -0
- openadapt_verify-0.2.0/tests/conftest.py +190 -0
- openadapt_verify-0.2.0/tests/test_adapters.py +111 -0
- openadapt_verify-0.2.0/tests/test_audit.py +430 -0
- openadapt_verify-0.2.0/tests/test_cli_mcp.py +183 -0
- openadapt_verify-0.2.0/tests/test_cli_protected_ledger.py +306 -0
- openadapt_verify-0.2.0/tests/test_cli_recheck_sticky.py +101 -0
- openadapt_verify-0.2.0/tests/test_cli_verdicts.py +299 -0
- openadapt_verify-0.2.0/tests/test_guard.py +83 -0
- openadapt_verify-0.2.0/tests/test_import_boundary.py +139 -0
- openadapt_verify-0.2.0/tests/test_ledger_integrity_hold.py +345 -0
- openadapt_verify-0.2.0/tests/test_mcp_protected_ledger.py +223 -0
- openadapt_verify-0.2.0/tests/test_mcp_recheck_sticky.py +107 -0
- openadapt_verify-0.2.0/tests/test_protected_ledger_capability.py +56 -0
- openadapt_verify-0.2.0/tests/test_quickstart.py +80 -0
- openadapt_verify-0.2.0/tests/test_verification.py +173 -0
- openadapt_verify-0.2.0/uv.lock +1678 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 MLDSAI Inc., Richard Abrich, and 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.
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: openadapt-verify
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Verify an agent's writes through an independent system-of-record read path
|
|
5
|
+
Project-URL: Repository, https://github.com/OpenAdaptAI/OpenAdapt/tree/main/packages/verify
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Python: <3.13,>=3.10
|
|
9
|
+
Requires-Dist: cryptography>=50.0.0
|
|
10
|
+
Requires-Dist: openadapt-flow[receipts]<4,>=3.0.0
|
|
11
|
+
Provides-Extra: claude
|
|
12
|
+
Requires-Dist: openadapt-flow[claude-agent-sdk]<4,>=3.0.0; extra == 'claude'
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
15
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
16
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
17
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
18
|
+
Provides-Extra: mcp
|
|
19
|
+
Requires-Dist: openadapt-flow[agent]<4,>=3.0.0; extra == 'mcp'
|
|
20
|
+
Provides-Extra: mysql
|
|
21
|
+
Requires-Dist: pymysql<2,>=1.1; extra == 'mysql'
|
|
22
|
+
Provides-Extra: openai
|
|
23
|
+
Requires-Dist: openadapt-flow[openai-agents]<4,>=3.0.0; extra == 'openai'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# openadapt-verify
|
|
27
|
+
|
|
28
|
+
Check whether an agent's write landed in the system of record, and that
|
|
29
|
+
nothing else changed. openadapt-verify reads the store before and after the
|
|
30
|
+
write through a read path the agent doesn't control (SQL, REST or FHIR), and
|
|
31
|
+
returns `CONFIRMED`, `REFUTED` or `UNCERTAIN` with a signed receipt. The
|
|
32
|
+
writer's own response is never evidence that the write persisted.
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
pip install openadapt-verify
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Requires Python 3.10, 3.11 or 3.12. The install is 21 distributions and about
|
|
39
|
+
41 MB of site-packages, with no vision, OCR, browser, GUI, NumPy or ML stack. It uses the verifier, write ledger, protected journal, SQL guards and
|
|
40
|
+
receipt signing from `openadapt-flow`: the same code, not a copy. Flow's
|
|
41
|
+
vision stack is its `vision` extra, which this package never installs.
|
|
42
|
+
|
|
43
|
+
## Five-minute quickstart
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
openadapt-verify quickstart
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
This starts disposable synthetic stores on 127.0.0.1, has a scripted agent
|
|
50
|
+
make three writes against each, and checks each write through an independent
|
|
51
|
+
read path:
|
|
52
|
+
|
|
53
|
+
| Agent write | Outcome |
|
|
54
|
+
|---|---|
|
|
55
|
+
| Saves the values it declared | `CONFIRMED` |
|
|
56
|
+
| Saves a different value than it declared | `REFUTED` |
|
|
57
|
+
| Reports success, but nothing reached the store | `UNCERTAIN` |
|
|
58
|
+
|
|
59
|
+
It runs the REST, SQLite and FHIR R4 read paths. Every result gets a portable
|
|
60
|
+
signed receipt (COSE_Sign1 with a local transparency log), and the quickstart
|
|
61
|
+
checks each receipt's signature. All state goes to a temporary directory that
|
|
62
|
+
is deleted afterwards. The keys are generated for the run; in a deployment,
|
|
63
|
+
pin the issuer and log keys you trust.
|
|
64
|
+
|
|
65
|
+
Add PostgreSQL or MySQL by pointing the quickstart at a **disposable**
|
|
66
|
+
database. It creates and drops its own table. The password comes from
|
|
67
|
+
`OPENADAPT_QUICKSTART_DB_PASSWORD`:
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
pip install 'openadapt-verify[mysql]' # PyMySQL, MIT
|
|
71
|
+
pip install 'psycopg[binary]' # LGPL-3.0; not a dependency of this package
|
|
72
|
+
openadapt-verify quickstart \
|
|
73
|
+
--postgres '{"host": "127.0.0.1", "port": 5432, "user": "qs", "dbname": "qs"}' \
|
|
74
|
+
--mysql '{"host": "127.0.0.1", "port": 3306, "user": "qs", "database": "qs"}'
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
A MySQL read must return utf8mb4 results. With any other result charset the
|
|
78
|
+
verifier refuses to read, so the quickstart reports `NOT SENT` and the agent
|
|
79
|
+
writes nothing.
|
|
80
|
+
|
|
81
|
+
The contracts the quickstart uses are in
|
|
82
|
+
[`quickstart.py`](src/openadapt_verify/quickstart.py). Each is an
|
|
83
|
+
[`openadapt.verify/v1` contract](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/VERIFIER_API.md);
|
|
84
|
+
copy one and point it at your store.
|
|
85
|
+
|
|
86
|
+
## Audit your automation's success check
|
|
87
|
+
|
|
88
|
+
`openadapt-verify audit` tells you how many wrong saves your own automation
|
|
89
|
+
reports as success. It puts an HTTP proxy in front of your app, runs your
|
|
90
|
+
command through it several times, and on each run changes one write. It then reads the store
|
|
91
|
+
through your contract's read path, never through the proxy, and counts the
|
|
92
|
+
runs where your command said it succeeded but the store disagrees.
|
|
93
|
+
|
|
94
|
+
```sh
|
|
95
|
+
openadapt-verify audit --demo
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The demo audits a script that trusts the save's 2xx reply, against the
|
|
99
|
+
quickstart's synthetic REST store (16 runs, about 20 seconds):
|
|
100
|
+
|
|
101
|
+
```text
|
|
102
|
+
fault runs bad saves check said OK caught false alarms
|
|
103
|
+
clean 2 0 0 0 0
|
|
104
|
+
no_persist 2 2 2 0 0
|
|
105
|
+
rejected 2 2 0 2 0
|
|
106
|
+
partial 2 2 2 0 0
|
|
107
|
+
wrong_value 2 2 2 0 0
|
|
108
|
+
duplicate 2 2 2 0 0
|
|
109
|
+
extra_write 2 2 2 0 0
|
|
110
|
+
lost_response 2 0 0 0 2
|
|
111
|
+
|
|
112
|
+
Your check (exit code 0) reported success on 10 of 12 bad saves.
|
|
113
|
+
False alarms: 0 of 2 clean runs, 2 on faulted runs whose write still landed correctly.
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`--demo readback` audits a script that also lists the items through the app
|
|
117
|
+
and looks for its own. It catches everything except `duplicate` and
|
|
118
|
+
`extra_write`: it finds its record and never asks what else was saved.
|
|
119
|
+
|
|
120
|
+
Point it at your own app:
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
openadapt-verify audit \
|
|
124
|
+
--upstream http://staging-app:8080 --listen 127.0.0.1:9000 \
|
|
125
|
+
--contract verify.yaml --params values.json --runs 16 \
|
|
126
|
+
--report audit.json -- npx playwright test save-order.spec.ts
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
- Run your command against `--listen` (the proxy) instead of the app. The
|
|
130
|
+
command gets `OPENADAPT_AUDIT_URL` (the proxy), `OPENADAPT_AUDIT_VALUES`
|
|
131
|
+
(the values this run should save, as JSON) and `OPENADAPT_AUDIT_RUN`. It is
|
|
132
|
+
never told which fault a run carries.
|
|
133
|
+
- `values.json` holds the values the command saves; `{run}` in a value becomes
|
|
134
|
+
the run number, so each run's record is distinct.
|
|
135
|
+
- The contract's read path must reach the store directly (the app's own API
|
|
136
|
+
at `--upstream`, or SQL). A contract that reads through the proxy is
|
|
137
|
+
refused.
|
|
138
|
+
- A run succeeds when the command exits 0, or with `--success-regex` when its
|
|
139
|
+
output matches.
|
|
140
|
+
- The fault goes to the run's first save: the first `POST`, `PUT`, `PATCH`
|
|
141
|
+
or `DELETE` whose JSON or form body carries one of the declared values. A
|
|
142
|
+
sign-in or other write before it passes through unchanged. With
|
|
143
|
+
`--write-path REGEX`, the first write whose path matches is the save
|
|
144
|
+
instead, body or not. `--write-method` narrows the methods. Later requests
|
|
145
|
+
in the run, retries included, pass through unchanged.
|
|
146
|
+
|
|
147
|
+
| Fault | What the proxy does to the run's first write |
|
|
148
|
+
|---|---|
|
|
149
|
+
| `no_persist` | Doesn't forward it; answers with the app's last real success reply |
|
|
150
|
+
| `rejected` | Doesn't forward it; answers 409 |
|
|
151
|
+
| `partial` | Blanks one declared value in the JSON or form body |
|
|
152
|
+
| `wrong_value` | Changes one declared value |
|
|
153
|
+
| `duplicate` | Forwards it twice; your command sees the first reply |
|
|
154
|
+
| `extra_write` | Forwards it, then a copy with one value changed |
|
|
155
|
+
| `lost_response` | Forwards it, then closes the connection without a reply |
|
|
156
|
+
| `wrong_record` | Rewrites a field naming the record (`--wrong-record FIELD=VALUE`) |
|
|
157
|
+
|
|
158
|
+
A bad save is any run whose store result isn't `CONFIRMED`. A false alarm is
|
|
159
|
+
a run whose write landed correctly but your command reported failure, such as
|
|
160
|
+
`lost_response` above. A run whose fault couldn't be applied (no write went
|
|
161
|
+
through the proxy, or no declared value was in the body) or whose store
|
|
162
|
+
couldn't be read is reported as not measured. The audit exits 0 when every bad
|
|
163
|
+
save was caught with no false alarms, 1 otherwise, and 2 when nothing was
|
|
164
|
+
measured.
|
|
165
|
+
|
|
166
|
+
Every fault is applied at the HTTP layer, so the audit needs no access to the
|
|
167
|
+
app's code. Two kinds from the verifier's fault set are not run, and the
|
|
168
|
+
report says so: `stale` (overwriting another user's concurrent change) and
|
|
169
|
+
`collateral_unaudited` (a change to a table the read path doesn't cover).
|
|
170
|
+
The faults are a fixed, hand-written set: the counts say which of these kinds
|
|
171
|
+
your check misses, not how often they happen in production.
|
|
172
|
+
|
|
173
|
+
Tested with two scripted HTTP clients against the quickstart's synthetic REST
|
|
174
|
+
store; it has not been run with a browser. The proxy's limits:
|
|
175
|
+
|
|
176
|
+
- Plain HTTP/1.x between your command and the proxy, one request per
|
|
177
|
+
connection. Each response is read in full before it is sent on, so
|
|
178
|
+
streamed responses arrive at once.
|
|
179
|
+
- No `Upgrade`: WebSocket and server-sent events don't pass through.
|
|
180
|
+
- Only `Location` headers that point at `--upstream` are rewritten to the
|
|
181
|
+
proxy. Absolute URLs in page bodies and cookie domains are left as they are.
|
|
182
|
+
- With an `https` upstream, the proxy itself serves plain HTTP, so a browser
|
|
183
|
+
drops the app's `Secure` cookies.
|
|
184
|
+
- `--listen 0.0.0.0` lets anyone who can reach the proxy write to the app;
|
|
185
|
+
the command warns. `--listen [::1]:9000` listens on IPv6 loopback.
|
|
186
|
+
|
|
187
|
+
## One line per write
|
|
188
|
+
|
|
189
|
+
Plain Python, as a decorator. The function's arguments are the contract's
|
|
190
|
+
values. The call returns the verifier's reply instead of raising, and a write
|
|
191
|
+
the verifier can't read first is never sent:
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
from openadapt_verify import verified
|
|
195
|
+
|
|
196
|
+
@verified("verify.yaml", pinned={"account": "account-17"})
|
|
197
|
+
def save_item(code, quantity, account):
|
|
198
|
+
api.post("/items", json={"code": code, "quantity": quantity})
|
|
199
|
+
|
|
200
|
+
reply = save_item(code="item-17", quantity="3", account="account-17")
|
|
201
|
+
reply["outcome"], reply["seal_path"]
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Or as a context manager, with a portable signed receipt:
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
from openadapt_verify import checked_write
|
|
208
|
+
|
|
209
|
+
with checked_write("verify.yaml", values) as write:
|
|
210
|
+
api.post("/items", json=values)
|
|
211
|
+
write.result.outcome, write.result.write_receipt_path
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Agent frameworks, each SDK behind its own extra:
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
from openadapt_verify.adapters.claude_sdk import verified # [claude]
|
|
218
|
+
tool = verified(save_order, contract="verify.yaml")
|
|
219
|
+
|
|
220
|
+
from openadapt_verify.adapters.openai_agents import verified # [openai]
|
|
221
|
+
tool = verified(save_order, contract="verify.yaml")
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
```sh
|
|
225
|
+
openadapt-verify serve --contracts ./contracts --state ./state # [mcp]
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
| Extra | Adds |
|
|
229
|
+
|---|---|
|
|
230
|
+
| `mysql` | PyMySQL, for a MySQL read path |
|
|
231
|
+
| `claude` | Claude Agent SDK tool adapter |
|
|
232
|
+
| `openai` | OpenAI Agents SDK tool adapter |
|
|
233
|
+
| `mcp` | The MCP server behind `openadapt-verify serve` |
|
|
234
|
+
|
|
235
|
+
SQLite is in the standard library. For PostgreSQL, install `psycopg`
|
|
236
|
+
yourself: the PostgreSQL drivers whose session the verifier can make
|
|
237
|
+
read-only are LGPL-3.0, so this package declares none.
|
|
238
|
+
|
|
239
|
+
## Wrap a write
|
|
240
|
+
|
|
241
|
+
Use an existing [`openadapt.verify/v1` contract](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/VERIFIER_API.md)
|
|
242
|
+
that reads your store through SQL, HTTP or another supported independent path.
|
|
243
|
+
Pin record and tenant identifiers that the agent must not choose.
|
|
244
|
+
|
|
245
|
+
```python
|
|
246
|
+
from openadapt_verify import WriteGuard
|
|
247
|
+
|
|
248
|
+
guard = WriteGuard("verify.yaml", pinned={"account": "account-17"})
|
|
249
|
+
reply = guard.run(values, lambda: existing_agent_write(values))
|
|
250
|
+
print(reply["outcome"], reply["seal"], reply["seal_path"])
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
The canonical guard reads before the write, refuses an unreadable baseline,
|
|
254
|
+
and reads again after the write, including an error or cancellation. It keeps
|
|
255
|
+
the existing uncertain-write ledger and retry protections. `arun` accepts an
|
|
256
|
+
async write. Only calls through the guard are checked; other tools and direct
|
|
257
|
+
calls remain outside it.
|
|
258
|
+
|
|
259
|
+
`CONFIRMED`, `REFUTED` and `UNCERTAIN` describe the store result. A confirmed
|
|
260
|
+
write can still be provisional when its contract requires a later recheck.
|
|
261
|
+
The contract's scope states what was read and what was not checked. It does
|
|
262
|
+
not establish that the agent chose an authorized or appropriate write.
|
|
263
|
+
|
|
264
|
+
## Two signed formats
|
|
265
|
+
|
|
266
|
+
`WriteGuard` and the SDK/MCP adapters keep their existing result format:
|
|
267
|
+
a confirmed effect receipt can have a **local Seal** at `seal_path`.
|
|
268
|
+
`seal: "sealed"` reports issuance; a store result can remain confirmed when
|
|
269
|
+
signing is off or fails. `verify_local_seal` checks that format against an
|
|
270
|
+
independently pinned key registry and the receipt file's SHA256.
|
|
271
|
+
|
|
272
|
+
For a **portable COSE/SCITT write receipt**, use the canonical verifier API:
|
|
273
|
+
|
|
274
|
+
```python
|
|
275
|
+
from openadapt_verify import Actor, Contract, begin_write, verify_write
|
|
276
|
+
|
|
277
|
+
pending = begin_write(Contract.load("verify.yaml"), values,
|
|
278
|
+
actor=Actor("agent", "existing-agent"), workdir="./state")
|
|
279
|
+
try:
|
|
280
|
+
existing_agent_write(values)
|
|
281
|
+
finally:
|
|
282
|
+
result = verify_write(pending, write_receipt=True)
|
|
283
|
+
print(result.outcome.value, result.write_receipt, result.write_receipt_path)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Check `write_receipt == "issued"` before using its path. `verify_receipt`
|
|
287
|
+
checks portable receipts with pinned issuer and log public keys and returns
|
|
288
|
+
`PortableReceiptVerification`; `verify_local_seal` returns
|
|
289
|
+
`LocalSealVerification`. A valid signature reports integrity and the pinned
|
|
290
|
+
issuer, separately from the payload's verdict. Never trust a key merely because
|
|
291
|
+
the receipt carries it. No receipt format or cryptography is defined here.
|
|
292
|
+
|
|
293
|
+
## Existing agents
|
|
294
|
+
|
|
295
|
+
The existing `WriteGuard` compatibility adapter emits Flow's
|
|
296
|
+
`DeprecationWarning` when first requested. Plain `import openadapt_verify`
|
|
297
|
+
does not load that adapter.
|
|
298
|
+
|
|
299
|
+
OpenAI and Claude use the existing tool adapters:
|
|
300
|
+
|
|
301
|
+
```sh
|
|
302
|
+
pip install 'openadapt-verify[openai]' # or [claude]
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Import `verified` from `openadapt_verify.adapters.openai_agents` or
|
|
306
|
+
`openadapt_verify.adapters.claude_sdk`, and pass an existing tool and
|
|
307
|
+
`contract="verify.yaml"`. The Claude module also exports the existing `only`
|
|
308
|
+
helper for restricting the agent's built-in tools.
|
|
309
|
+
|
|
310
|
+
For browser-use, wrap a typed async action **before** registering it with
|
|
311
|
+
`Tools.action`:
|
|
312
|
+
|
|
313
|
+
```python
|
|
314
|
+
from openadapt_verify.adapters.browser_use import verified
|
|
315
|
+
|
|
316
|
+
checked_save = verified(save_action, contract="verify.yaml")
|
|
317
|
+
tools.action("Save the item")(checked_save)
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Parameter names and annotations, including `browser_session`, are preserved.
|
|
321
|
+
The action returns the verifier reply as tool text. Other browser actions are
|
|
322
|
+
unchanged. See the [official custom-action API](https://docs.browser-use.com/open-source/customize/tools/add).
|
|
323
|
+
|
|
324
|
+
For a caller-owned Stagehand Python session:
|
|
325
|
+
|
|
326
|
+
```python
|
|
327
|
+
from openadapt_verify.adapters.stagehand import verified
|
|
328
|
+
|
|
329
|
+
checked = verified(session, contract="verify.yaml")
|
|
330
|
+
reply = await checked.act(values=values, input=observed_action)
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
This checks that single `act` call. It does not replace the original session
|
|
334
|
+
or wrap its other methods. See the [official Python session API](https://docs.stagehand.dev/v3/sdk/python).
|
|
335
|
+
Tests use scripted SDK callbacks and local stores; they do not establish live
|
|
336
|
+
model/provider behavior.
|
|
337
|
+
|
|
338
|
+
## Commands and MCP
|
|
339
|
+
|
|
340
|
+
```sh
|
|
341
|
+
openadapt-verify run --contract verify.yaml --params values.json --state ./state -- python write.py
|
|
342
|
+
openadapt-verify verify write.receipt.cbor --issuer-key issuer.pub.pem --log-key log.pub.pem
|
|
343
|
+
openadapt-verify recheck --contract verify.yaml --params values.json --recheck-state ./state/WRITE_ID/recheck.json
|
|
344
|
+
pip install 'openadapt-verify[mcp]'
|
|
345
|
+
openadapt-verify serve --contracts ./contracts --state ./state
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
`run` executes an explicit argv without a shell. It reads back and requests a
|
|
349
|
+
portable receipt even when the command fails. Exit zero requires a final
|
|
350
|
+
confirmed result, issued portable receipt and command exit zero. A provisional
|
|
351
|
+
result exits 5 and reports `recheck_state_path`. Command diagnostics go to stderr.
|
|
352
|
+
|
|
353
|
+
`verify` checks the portable signature and log inclusion against the pinned
|
|
354
|
+
keys. Its JSON `valid` field reports those integrity checks; the exit code
|
|
355
|
+
also requires a final `CONFIRMED` payload. Both commands use 0 for final
|
|
356
|
+
`CONFIRMED`, 3 for `UNCERTAIN`, 4 for `REFUTED` and 5 for provisional confirmation.
|
|
357
|
+
Code 1 reports a signature, signing or command failure; 2 reports a usage,
|
|
358
|
+
configuration or read error. Inspect the JSON outcome and signing fields
|
|
359
|
+
separately before deciding what happened.
|
|
360
|
+
|
|
361
|
+
At or after `recheck_due_at`, `recheck` reads the store using the canonical
|
|
362
|
+
verifier and the private state saved by `run`. Supply the original contract,
|
|
363
|
+
params and `--agent-id`, and retain the same verifier authority and ledger
|
|
364
|
+
directories. The ledger directory must not be writable by the checked agent.
|
|
365
|
+
Before the due time it exits 5 without ending the window.
|
|
366
|
+
A confirmed recheck exits 0, an uncertain one 3 and a refuted one 4. It reports
|
|
367
|
+
the current result and read window as unsigned JSON; the earlier signed
|
|
368
|
+
receipt remains provisional. A write after the recheck read is outside its
|
|
369
|
+
window. A refusal requires a person to check the record before any retry.
|
|
370
|
+
|
|
371
|
+
`serve` exposes the existing `verify_contracts`, `verify_begin`, `verify_check`
|
|
372
|
+
and `verify_recheck` MCP tools. Their contracts remain operator-owned, and a
|
|
373
|
+
refusal never authorizes retrying the write. The `openadapt_verify.shadow`
|
|
374
|
+
module also exposes the canonical shadow verifier without changing its scope.
|
|
375
|
+
|
|
376
|
+
To configure the same protected journal for `run`, `recheck`, and `serve`,
|
|
377
|
+
see [Use the protected journal from installed tools](../flow/docs/VERIFIER_API.md#use-the-protected-journal-from-installed-tools).
|
|
378
|
+
An enrolled journal refuses if a process opens it without its authority.
|
|
379
|
+
The legacy default provides no tamper evidence when the checked agent can
|
|
380
|
+
write to its journal directory.
|
|
381
|
+
|
|
382
|
+
A recheck exits zero only after the exact ledger attempt records confirmation for the same receipt. A prior refutation remains held for person review even if the unexplained change is later removed. A transient unreadable-store recheck can recover after a fresh read of the same originally confirmed write.
|