arcezia 1.0.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.
- arcezia-1.0.0/LICENSE +22 -0
- arcezia-1.0.0/PKG-INFO +345 -0
- arcezia-1.0.0/README.md +289 -0
- arcezia-1.0.0/arcezia/__init__.py +118 -0
- arcezia-1.0.0/arcezia/client.py +725 -0
- arcezia-1.0.0/arcezia/integrations/__init__.py +12 -0
- arcezia-1.0.0/arcezia/integrations/_common.py +36 -0
- arcezia-1.0.0/arcezia/integrations/anthropic.py +288 -0
- arcezia-1.0.0/arcezia/integrations/autogen.py +299 -0
- arcezia-1.0.0/arcezia/integrations/claude_code.py +234 -0
- arcezia-1.0.0/arcezia/integrations/langchain.py +321 -0
- arcezia-1.0.0/arcezia/integrations/llamaindex.py +142 -0
- arcezia-1.0.0/arcezia/integrations/n8n.py +300 -0
- arcezia-1.0.0/arcezia/integrations/openai.py +350 -0
- arcezia-1.0.0/arcezia/integrations/openclaw.py +404 -0
- arcezia-1.0.0/arcezia/integrations/universal.py +179 -0
- arcezia-1.0.0/arcezia.egg-info/PKG-INFO +345 -0
- arcezia-1.0.0/arcezia.egg-info/SOURCES.txt +28 -0
- arcezia-1.0.0/arcezia.egg-info/dependency_links.txt +1 -0
- arcezia-1.0.0/arcezia.egg-info/entry_points.txt +2 -0
- arcezia-1.0.0/arcezia.egg-info/requires.txt +44 -0
- arcezia-1.0.0/arcezia.egg-info/top_level.txt +1 -0
- arcezia-1.0.0/pyproject.toml +65 -0
- arcezia-1.0.0/setup.cfg +4 -0
- arcezia-1.0.0/tests/test_claude_code_hook.py +145 -0
- arcezia-1.0.0/tests/test_client.py +606 -0
- arcezia-1.0.0/tests/test_docs_match_api.py +271 -0
- arcezia-1.0.0/tests/test_integrations.py +1174 -0
- arcezia-1.0.0/tests/test_n8n.py +72 -0
- arcezia-1.0.0/tests/test_openclaw.py +198 -0
arcezia-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2026 Arcezia
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
arcezia-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: arcezia
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Runtime safety enforcement for autonomous AI agents — official Python SDK
|
|
5
|
+
Author-email: Praveen Dante <praveen@arcezia.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://arcezia.com
|
|
8
|
+
Project-URL: Documentation, https://arcezia.com/developer-docs
|
|
9
|
+
Project-URL: Repository, https://github.com/Praveendante/arcezia-python
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/Praveendante/arcezia-python/issues
|
|
11
|
+
Keywords: ai-safety,agents,langchain,openai,anthropic,llm,guardrails,runtime-safety,ai-agents,safety-verification
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
|
+
Classifier: Topic :: Security
|
|
16
|
+
Classifier: Development Status :: 4 - Beta
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Provides-Extra: httpx
|
|
21
|
+
Requires-Dist: httpx>=0.27; extra == "httpx"
|
|
22
|
+
Provides-Extra: langchain
|
|
23
|
+
Requires-Dist: langchain>=0.2; extra == "langchain"
|
|
24
|
+
Requires-Dist: langchain-core>=0.2; extra == "langchain"
|
|
25
|
+
Requires-Dist: httpx>=0.27; extra == "langchain"
|
|
26
|
+
Provides-Extra: langgraph
|
|
27
|
+
Requires-Dist: langgraph>=0.2; extra == "langgraph"
|
|
28
|
+
Requires-Dist: langchain-core>=0.2; extra == "langgraph"
|
|
29
|
+
Requires-Dist: httpx>=0.27; extra == "langgraph"
|
|
30
|
+
Provides-Extra: openai
|
|
31
|
+
Requires-Dist: openai>=1.30; extra == "openai"
|
|
32
|
+
Requires-Dist: httpx>=0.27; extra == "openai"
|
|
33
|
+
Provides-Extra: anthropic
|
|
34
|
+
Requires-Dist: anthropic>=0.28; extra == "anthropic"
|
|
35
|
+
Requires-Dist: httpx>=0.27; extra == "anthropic"
|
|
36
|
+
Provides-Extra: crewai
|
|
37
|
+
Requires-Dist: crewai>=0.30; extra == "crewai"
|
|
38
|
+
Requires-Dist: httpx>=0.27; extra == "crewai"
|
|
39
|
+
Provides-Extra: llamaindex
|
|
40
|
+
Requires-Dist: llama-index-core>=0.11; extra == "llamaindex"
|
|
41
|
+
Requires-Dist: httpx>=0.27; extra == "llamaindex"
|
|
42
|
+
Provides-Extra: autogen
|
|
43
|
+
Requires-Dist: autogen-agentchat>=0.4; extra == "autogen"
|
|
44
|
+
Requires-Dist: httpx>=0.27; extra == "autogen"
|
|
45
|
+
Provides-Extra: all
|
|
46
|
+
Requires-Dist: httpx>=0.27; extra == "all"
|
|
47
|
+
Requires-Dist: langchain>=0.2; extra == "all"
|
|
48
|
+
Requires-Dist: langchain-core>=0.2; extra == "all"
|
|
49
|
+
Requires-Dist: langgraph>=0.2; extra == "all"
|
|
50
|
+
Requires-Dist: openai>=1.30; extra == "all"
|
|
51
|
+
Requires-Dist: anthropic>=0.28; extra == "all"
|
|
52
|
+
Requires-Dist: crewai>=0.30; extra == "all"
|
|
53
|
+
Requires-Dist: llama-index-core>=0.11; extra == "all"
|
|
54
|
+
Requires-Dist: autogen-agentchat>=0.4; extra == "all"
|
|
55
|
+
Dynamic: license-file
|
|
56
|
+
|
|
57
|
+
# arcezia
|
|
58
|
+
|
|
59
|
+
Runtime safety verification for autonomous AI agents — official Python SDK.
|
|
60
|
+
|
|
61
|
+
All verification runs in Arcezia's secure cloud. The SDK makes HTTPS calls to
|
|
62
|
+
`api.arcezia.com` and returns typed result objects. Zero inference on the client.
|
|
63
|
+
|
|
64
|
+
## Install
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install arcezia
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Framework extras:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pip install "arcezia[langchain]" # LangChain + LangGraph
|
|
74
|
+
pip install "arcezia[openai]" # OpenAI Agents SDK
|
|
75
|
+
pip install "arcezia[anthropic]" # Anthropic (Claude) SDK
|
|
76
|
+
pip install "arcezia[autogen]" # AutoGen (legacy + modern)
|
|
77
|
+
pip install "arcezia[llamaindex]" # LlamaIndex
|
|
78
|
+
pip install "arcezia[all]" # everything
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Quick start
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
import arcezia
|
|
85
|
+
|
|
86
|
+
az = arcezia.Arcezia(api_key="ar_live_...", task="clean up test records")
|
|
87
|
+
|
|
88
|
+
cert = az.verify(
|
|
89
|
+
action_type="execute_sql",
|
|
90
|
+
action_description="DELETE FROM analytics_staging WHERE date < '2024-01-01'",
|
|
91
|
+
domain="database_ops",
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if cert.degraded:
|
|
95
|
+
# Arcezia could not be reached, so nothing was actually verified.
|
|
96
|
+
# The default on_error="fail_closed" raises before you get here; check this
|
|
97
|
+
# explicitly if you set on_error="review" or "fail_open".
|
|
98
|
+
raise RuntimeError("Not verified — Arcezia unreachable")
|
|
99
|
+
|
|
100
|
+
# Gate on ALLOW positively — never on "not blocked". A verdict can be
|
|
101
|
+
# review (insufficient evidence, human confirmation required), which is
|
|
102
|
+
# neither allow nor block; treating it as runnable executes an action the
|
|
103
|
+
# engine explicitly declined to clear. cert.allow is True only for a
|
|
104
|
+
# grounded ALLOW.
|
|
105
|
+
if not cert.allow:
|
|
106
|
+
raise RuntimeError(f"Not allowed ({'review' if cert.review else 'blocked'}): {cert.summary}")
|
|
107
|
+
|
|
108
|
+
db.execute(sql) # only reached when the verdict is ALLOW
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The framework adapters below do this for you: a degraded certificate always
|
|
112
|
+
raises `ArceziaUnavailableError` and the tool never executes.
|
|
113
|
+
|
|
114
|
+
## Framework integrations
|
|
115
|
+
|
|
116
|
+
**LangChain / LangGraph**
|
|
117
|
+
```python
|
|
118
|
+
from arcezia.integrations.langchain import ArceziaToolkit
|
|
119
|
+
|
|
120
|
+
toolkit = ArceziaToolkit(az)
|
|
121
|
+
safe_tools = toolkit.wrap(tools) # classic AgentExecutor
|
|
122
|
+
safe_tools = toolkit.wrap_for_langgraph(tools) # LangGraph / tool-calling
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**OpenAI function calling**
|
|
126
|
+
```python
|
|
127
|
+
from arcezia.integrations.openai import ArceziaGuard
|
|
128
|
+
|
|
129
|
+
guard = ArceziaGuard(az)
|
|
130
|
+
result = guard.execute_tool_call(
|
|
131
|
+
tool_call=response.choices[0].message.tool_calls[0],
|
|
132
|
+
tool_implementations={"execute_sql": db.execute},
|
|
133
|
+
)
|
|
134
|
+
# or wrap a single function:
|
|
135
|
+
safe_execute = guard.wrap_function("execute_sql", db.execute)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**CrewAI**
|
|
139
|
+
```python
|
|
140
|
+
from arcezia.integrations.openai import ArceziaCrewTool
|
|
141
|
+
|
|
142
|
+
class SafeSQLTool(ArceziaCrewTool):
|
|
143
|
+
az = your_arcezia_client
|
|
144
|
+
domain = "database_ops"
|
|
145
|
+
name = "execute_sql"
|
|
146
|
+
description = "Execute SQL"
|
|
147
|
+
|
|
148
|
+
def _run(self, sql: str) -> str:
|
|
149
|
+
return db.execute(sql)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Anthropic (Claude tool_use)**
|
|
153
|
+
```python
|
|
154
|
+
from arcezia.integrations.anthropic import ArceziaAnthropicGuard
|
|
155
|
+
|
|
156
|
+
guard = ArceziaAnthropicGuard(az)
|
|
157
|
+
safe_uses, blocked = guard.filter_tool_uses(message.content)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**AutoGen**
|
|
161
|
+
```python
|
|
162
|
+
from arcezia.integrations.autogen import ArceziaAutoGenGuard
|
|
163
|
+
|
|
164
|
+
guard = ArceziaAutoGenGuard(az)
|
|
165
|
+
safe_fn = guard.wrap("execute_sql", db.execute, "database_ops") # name first
|
|
166
|
+
safe_map = guard.wrap_many([ # list of tuples
|
|
167
|
+
("execute_sql", db.execute, "database_ops"),
|
|
168
|
+
("send_data", exporter.send, "agent_action"),
|
|
169
|
+
])
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**LlamaIndex**
|
|
173
|
+
```python
|
|
174
|
+
from arcezia.integrations.llamaindex import ArceziaLlamaToolkit
|
|
175
|
+
safe_tools = ArceziaLlamaToolkit(az).wrap(tools)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Any framework** (Pydantic AI, smolagents, Google ADK, Strands, …)
|
|
179
|
+
```python
|
|
180
|
+
from arcezia import guard_callable
|
|
181
|
+
safe_fn = guard_callable(run_sql, az)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**Claude Code CLI hook** (gated at the harness level — every tool call)
|
|
185
|
+
```bash
|
|
186
|
+
arcezia-hook install # writes PreToolUse hook to ~/.claude/settings.json
|
|
187
|
+
export ARCEZIA_API_KEY=ar_live_...
|
|
188
|
+
export TASK="refactor auth module"
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Generic dispatch-loop agents (OpenCLAW, AutoAgent, …)**
|
|
192
|
+
```python
|
|
193
|
+
from arcezia.integrations.openclaw import DispatchGuard
|
|
194
|
+
guard = DispatchGuard(api_key="ar_live_...", task="...")
|
|
195
|
+
result = guard.dispatch("write_file", {"path": "/etc/app.conf", "content": "..."})
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**n8n workflows**
|
|
199
|
+
```python
|
|
200
|
+
from arcezia.integrations.n8n import workflow_template, save_template
|
|
201
|
+
save_template("arcezia_gate.json") # import into n8n
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Verdicts
|
|
205
|
+
|
|
206
|
+
| Verdict | Meaning |
|
|
207
|
+
|---|---|
|
|
208
|
+
| `cert.allow` | Safe to execute — all required evidence is grounded |
|
|
209
|
+
| `cert.block` | Execution blocked — violated constraint or fabrication detected |
|
|
210
|
+
| `cert.review` | Insufficient evidence — human confirmation required |
|
|
211
|
+
|
|
212
|
+
Two scores travel with every certificate — they measure different things:
|
|
213
|
+
|
|
214
|
+
| Field | Meaning |
|
|
215
|
+
|---|---|
|
|
216
|
+
| `cert.precondition_score` | `[0,1]` severity-weighted fraction of required preconditions **satisfied** |
|
|
217
|
+
| `cert.trust_score` | `[0,1]` fraction of evidence that is **externally grounded**, not agent-claimed |
|
|
218
|
+
|
|
219
|
+
## The four levels
|
|
220
|
+
|
|
221
|
+
Each level is useful on its own and assumes the one below it. Every framework
|
|
222
|
+
adapter implements **Level 1** for you; Levels 2–4 are reached through the
|
|
223
|
+
adapter's `.az` property — the same client, no private access.
|
|
224
|
+
|
|
225
|
+
| Level | What you get | How |
|
|
226
|
+
|---|---|---|
|
|
227
|
+
| **1 — Drop-in gating** | Every tool call verified before it runs | `toolkit.wrap(tools)` |
|
|
228
|
+
| **2 — Chain verification** | Verify the whole *plan*, not just each step | `toolkit.az.verify_chain(...)` |
|
|
229
|
+
| **3 — Grounded evidence** | Arcezia asks *your* systems for facts instead of trusting the agent | register a probe webhook |
|
|
230
|
+
| **4 — Custom domains** | Your own constraint domains and compliance packs | `POST /v1/domains` |
|
|
231
|
+
|
|
232
|
+
**Level 2 — verify the plan before running any of it**
|
|
233
|
+
```python
|
|
234
|
+
result = toolkit.az.verify_chain({
|
|
235
|
+
"steps": [
|
|
236
|
+
{"id": "s1", "action_type": "execute_sql", "domain": "database_ops",
|
|
237
|
+
"action_description": "SELECT ssn, name FROM customers"},
|
|
238
|
+
{"id": "s2", "action_type": "send_email", "domain": "email_ops",
|
|
239
|
+
"action_description": "email the list to external-analytics@gmail.com"},
|
|
240
|
+
]
|
|
241
|
+
}, stop_on_block=True)
|
|
242
|
+
# → overall_verdict "SEMANTIC_BLOCK", blocked_at "s2",
|
|
243
|
+
# semantic_triggers [{"pattern_name": "structural_exfiltration", ...}]
|
|
244
|
+
|
|
245
|
+
# Response: {overall_verdict, blocked_at, steps[], semantic_triggers,
|
|
246
|
+
# final_state, session_state_updated}
|
|
247
|
+
# There is no top-level "verdict" — per-step verdicts live under steps[].
|
|
248
|
+
if result["overall_verdict"] != "SAFE":
|
|
249
|
+
# blocked_at names the step only when execution was actually stopped.
|
|
250
|
+
# On REVIEW_REQUIRED nothing was blocked, so it is null — find the step
|
|
251
|
+
# that needs attention in steps[] instead.
|
|
252
|
+
step = result["blocked_at"] or next(
|
|
253
|
+
(s["id"] for s in result["steps"] if s["verdict"] != "ALLOW"), None
|
|
254
|
+
)
|
|
255
|
+
abort(step)
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
`overall_verdict` is one of:
|
|
259
|
+
|
|
260
|
+
| Value | Meaning | `blocked_at` |
|
|
261
|
+
|---|---|---|
|
|
262
|
+
| `SAFE` | every step cleared | `null` |
|
|
263
|
+
| `BLOCKED` | a single step was blocked on its own merits | the step id |
|
|
264
|
+
| `SEMANTIC_BLOCK` | the steps are individually fine but **compose** into harm — check `semantic_triggers` (e.g. `structural_exfiltration`, `credential_exfiltration`, `recon_then_exfil`) | the step id |
|
|
265
|
+
| `REVIEW_REQUIRED` | a step needs evidence or human approval | `null` — nothing was blocked |
|
|
266
|
+
|
|
267
|
+
> **Describe the artefact, not just the operation.** Arcezia grounds its
|
|
268
|
+
> verdicts on concrete referents in the description — file paths, URLs,
|
|
269
|
+
> recipient addresses, credential and PII field names. `"SELECT ssn, name FROM
|
|
270
|
+
> customers"` names PII, so the read grounds as sensitive access and the chain
|
|
271
|
+
> above composes into `SEMANTIC_BLOCK`. `"SELECT email, name FROM customers"`
|
|
272
|
+
> names only column identifiers, so the same chain returns `REVIEW_REQUIRED`
|
|
273
|
+
> instead: still not `SAFE`, still not executable, but held for a human rather
|
|
274
|
+
> than positively identified as exfiltration.
|
|
275
|
+
>
|
|
276
|
+
> The rule this reflects: **a vague description degrades a verdict toward
|
|
277
|
+
> review — never toward approval.** Arcezia never allows what it could not
|
|
278
|
+
> verify, so imprecision costs you review latency, not safety. The framework
|
|
279
|
+
> adapters get this right automatically because they pass the real tool
|
|
280
|
+
> arguments; it is worth attention only when you hand-build chain manifests.
|
|
281
|
+
|
|
282
|
+
Chain steps do **not** inherit the session's capability envelope, so
|
|
283
|
+
`action_within_task_scope` stays unresolved and a chain will not reach `SAFE`
|
|
284
|
+
on the envelope alone. Ground it per step with an `evidence` dict (the key is
|
|
285
|
+
`evidence` — `agent_evidence` is ignored on chain steps):
|
|
286
|
+
|
|
287
|
+
```python
|
|
288
|
+
{"id": "s1", "action_type": "execute_sql", "domain": "database_ops",
|
|
289
|
+
"action_description": "SELECT ssn, name FROM customers",
|
|
290
|
+
"evidence": {"action_within_task_scope": True}}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
`id` and `step_id` are accepted interchangeably. Note that `state_mutations`
|
|
294
|
+
may only *add* danger, never remove it: asserting a danger flag `True` is
|
|
295
|
+
accepted, asserting it `False` is rejected, and flags the engine derives for
|
|
296
|
+
itself (the `g_*` world-state namespace) are not caller-writable at all.
|
|
297
|
+
|
|
298
|
+
Audit after execution — did reality match the prediction?
|
|
299
|
+
```python
|
|
300
|
+
toolkit.az.verify_outcome(
|
|
301
|
+
action_type="execute_sql",
|
|
302
|
+
action_description="DELETE FROM orders WHERE test = true",
|
|
303
|
+
outcome={"rows_affected": 50000}, # what ACTUALLY happened
|
|
304
|
+
expected={"rows_affected": 1}, # what you intended
|
|
305
|
+
)
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
**Level 3 — ground the evidence.** Register a probe webhook so evidence is
|
|
309
|
+
`GROUNDED` rather than `CLAIMED`. Human intent can never be produced by a model,
|
|
310
|
+
so ground it explicitly:
|
|
311
|
+
```python
|
|
312
|
+
toolkit.az.authorize(user_token) # user_explicit_authorization
|
|
313
|
+
toolkit.az.authorize_production(prod_token) # production_explicit_authorization
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
These ground **different** constraints. Actions touching production generally
|
|
317
|
+
need both — `authorize()` alone will leave `production_explicit_authorization`
|
|
318
|
+
unresolved and the action stays in `REVIEW`.
|
|
319
|
+
|
|
320
|
+
> **Integrating over raw HTTP** (n8n, curl, another language)? Two things the
|
|
321
|
+
> SDK handles for you: the API is behind a WAF that rejects the default
|
|
322
|
+
> library agent strings (e.g. `Python-urllib/*`), so send an explicit
|
|
323
|
+
> `User-Agent` of your own; and the verify response field on the wire is
|
|
324
|
+
> `dc_score` — the SDK exposes it as `cert.precondition_score`.
|
|
325
|
+
|
|
326
|
+
Full guide: [arcezia.com/docs](https://arcezia.com/developer-docs)
|
|
327
|
+
|
|
328
|
+
## Development mode
|
|
329
|
+
|
|
330
|
+
Use an `ar_test_` key for local development — infrastructure constraints
|
|
331
|
+
(backup APIs, capability envelopes, CI gates) are relaxed so you are not blocked
|
|
332
|
+
by production infra that does not exist on your laptop:
|
|
333
|
+
|
|
334
|
+
```python
|
|
335
|
+
az = arcezia.Arcezia(api_key="ar_test_...", task="...") # dev mode by default
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Development mode is only available on `ar_test_` keys and is re-checked
|
|
339
|
+
server-side. Live `ar_live_` keys are always pinned to production and cannot
|
|
340
|
+
point at `localhost`.
|
|
341
|
+
|
|
342
|
+
## Links
|
|
343
|
+
|
|
344
|
+
- [Developer documentation](https://arcezia.com/developer-docs)
|
|
345
|
+
- [Dashboard](https://app.arcezia.com)
|
arcezia-1.0.0/README.md
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# arcezia
|
|
2
|
+
|
|
3
|
+
Runtime safety verification for autonomous AI agents — official Python SDK.
|
|
4
|
+
|
|
5
|
+
All verification runs in Arcezia's secure cloud. The SDK makes HTTPS calls to
|
|
6
|
+
`api.arcezia.com` and returns typed result objects. Zero inference on the client.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install arcezia
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Framework extras:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install "arcezia[langchain]" # LangChain + LangGraph
|
|
18
|
+
pip install "arcezia[openai]" # OpenAI Agents SDK
|
|
19
|
+
pip install "arcezia[anthropic]" # Anthropic (Claude) SDK
|
|
20
|
+
pip install "arcezia[autogen]" # AutoGen (legacy + modern)
|
|
21
|
+
pip install "arcezia[llamaindex]" # LlamaIndex
|
|
22
|
+
pip install "arcezia[all]" # everything
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick start
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
import arcezia
|
|
29
|
+
|
|
30
|
+
az = arcezia.Arcezia(api_key="ar_live_...", task="clean up test records")
|
|
31
|
+
|
|
32
|
+
cert = az.verify(
|
|
33
|
+
action_type="execute_sql",
|
|
34
|
+
action_description="DELETE FROM analytics_staging WHERE date < '2024-01-01'",
|
|
35
|
+
domain="database_ops",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
if cert.degraded:
|
|
39
|
+
# Arcezia could not be reached, so nothing was actually verified.
|
|
40
|
+
# The default on_error="fail_closed" raises before you get here; check this
|
|
41
|
+
# explicitly if you set on_error="review" or "fail_open".
|
|
42
|
+
raise RuntimeError("Not verified — Arcezia unreachable")
|
|
43
|
+
|
|
44
|
+
# Gate on ALLOW positively — never on "not blocked". A verdict can be
|
|
45
|
+
# review (insufficient evidence, human confirmation required), which is
|
|
46
|
+
# neither allow nor block; treating it as runnable executes an action the
|
|
47
|
+
# engine explicitly declined to clear. cert.allow is True only for a
|
|
48
|
+
# grounded ALLOW.
|
|
49
|
+
if not cert.allow:
|
|
50
|
+
raise RuntimeError(f"Not allowed ({'review' if cert.review else 'blocked'}): {cert.summary}")
|
|
51
|
+
|
|
52
|
+
db.execute(sql) # only reached when the verdict is ALLOW
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The framework adapters below do this for you: a degraded certificate always
|
|
56
|
+
raises `ArceziaUnavailableError` and the tool never executes.
|
|
57
|
+
|
|
58
|
+
## Framework integrations
|
|
59
|
+
|
|
60
|
+
**LangChain / LangGraph**
|
|
61
|
+
```python
|
|
62
|
+
from arcezia.integrations.langchain import ArceziaToolkit
|
|
63
|
+
|
|
64
|
+
toolkit = ArceziaToolkit(az)
|
|
65
|
+
safe_tools = toolkit.wrap(tools) # classic AgentExecutor
|
|
66
|
+
safe_tools = toolkit.wrap_for_langgraph(tools) # LangGraph / tool-calling
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**OpenAI function calling**
|
|
70
|
+
```python
|
|
71
|
+
from arcezia.integrations.openai import ArceziaGuard
|
|
72
|
+
|
|
73
|
+
guard = ArceziaGuard(az)
|
|
74
|
+
result = guard.execute_tool_call(
|
|
75
|
+
tool_call=response.choices[0].message.tool_calls[0],
|
|
76
|
+
tool_implementations={"execute_sql": db.execute},
|
|
77
|
+
)
|
|
78
|
+
# or wrap a single function:
|
|
79
|
+
safe_execute = guard.wrap_function("execute_sql", db.execute)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**CrewAI**
|
|
83
|
+
```python
|
|
84
|
+
from arcezia.integrations.openai import ArceziaCrewTool
|
|
85
|
+
|
|
86
|
+
class SafeSQLTool(ArceziaCrewTool):
|
|
87
|
+
az = your_arcezia_client
|
|
88
|
+
domain = "database_ops"
|
|
89
|
+
name = "execute_sql"
|
|
90
|
+
description = "Execute SQL"
|
|
91
|
+
|
|
92
|
+
def _run(self, sql: str) -> str:
|
|
93
|
+
return db.execute(sql)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Anthropic (Claude tool_use)**
|
|
97
|
+
```python
|
|
98
|
+
from arcezia.integrations.anthropic import ArceziaAnthropicGuard
|
|
99
|
+
|
|
100
|
+
guard = ArceziaAnthropicGuard(az)
|
|
101
|
+
safe_uses, blocked = guard.filter_tool_uses(message.content)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**AutoGen**
|
|
105
|
+
```python
|
|
106
|
+
from arcezia.integrations.autogen import ArceziaAutoGenGuard
|
|
107
|
+
|
|
108
|
+
guard = ArceziaAutoGenGuard(az)
|
|
109
|
+
safe_fn = guard.wrap("execute_sql", db.execute, "database_ops") # name first
|
|
110
|
+
safe_map = guard.wrap_many([ # list of tuples
|
|
111
|
+
("execute_sql", db.execute, "database_ops"),
|
|
112
|
+
("send_data", exporter.send, "agent_action"),
|
|
113
|
+
])
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**LlamaIndex**
|
|
117
|
+
```python
|
|
118
|
+
from arcezia.integrations.llamaindex import ArceziaLlamaToolkit
|
|
119
|
+
safe_tools = ArceziaLlamaToolkit(az).wrap(tools)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Any framework** (Pydantic AI, smolagents, Google ADK, Strands, …)
|
|
123
|
+
```python
|
|
124
|
+
from arcezia import guard_callable
|
|
125
|
+
safe_fn = guard_callable(run_sql, az)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Claude Code CLI hook** (gated at the harness level — every tool call)
|
|
129
|
+
```bash
|
|
130
|
+
arcezia-hook install # writes PreToolUse hook to ~/.claude/settings.json
|
|
131
|
+
export ARCEZIA_API_KEY=ar_live_...
|
|
132
|
+
export TASK="refactor auth module"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Generic dispatch-loop agents (OpenCLAW, AutoAgent, …)**
|
|
136
|
+
```python
|
|
137
|
+
from arcezia.integrations.openclaw import DispatchGuard
|
|
138
|
+
guard = DispatchGuard(api_key="ar_live_...", task="...")
|
|
139
|
+
result = guard.dispatch("write_file", {"path": "/etc/app.conf", "content": "..."})
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**n8n workflows**
|
|
143
|
+
```python
|
|
144
|
+
from arcezia.integrations.n8n import workflow_template, save_template
|
|
145
|
+
save_template("arcezia_gate.json") # import into n8n
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Verdicts
|
|
149
|
+
|
|
150
|
+
| Verdict | Meaning |
|
|
151
|
+
|---|---|
|
|
152
|
+
| `cert.allow` | Safe to execute — all required evidence is grounded |
|
|
153
|
+
| `cert.block` | Execution blocked — violated constraint or fabrication detected |
|
|
154
|
+
| `cert.review` | Insufficient evidence — human confirmation required |
|
|
155
|
+
|
|
156
|
+
Two scores travel with every certificate — they measure different things:
|
|
157
|
+
|
|
158
|
+
| Field | Meaning |
|
|
159
|
+
|---|---|
|
|
160
|
+
| `cert.precondition_score` | `[0,1]` severity-weighted fraction of required preconditions **satisfied** |
|
|
161
|
+
| `cert.trust_score` | `[0,1]` fraction of evidence that is **externally grounded**, not agent-claimed |
|
|
162
|
+
|
|
163
|
+
## The four levels
|
|
164
|
+
|
|
165
|
+
Each level is useful on its own and assumes the one below it. Every framework
|
|
166
|
+
adapter implements **Level 1** for you; Levels 2–4 are reached through the
|
|
167
|
+
adapter's `.az` property — the same client, no private access.
|
|
168
|
+
|
|
169
|
+
| Level | What you get | How |
|
|
170
|
+
|---|---|---|
|
|
171
|
+
| **1 — Drop-in gating** | Every tool call verified before it runs | `toolkit.wrap(tools)` |
|
|
172
|
+
| **2 — Chain verification** | Verify the whole *plan*, not just each step | `toolkit.az.verify_chain(...)` |
|
|
173
|
+
| **3 — Grounded evidence** | Arcezia asks *your* systems for facts instead of trusting the agent | register a probe webhook |
|
|
174
|
+
| **4 — Custom domains** | Your own constraint domains and compliance packs | `POST /v1/domains` |
|
|
175
|
+
|
|
176
|
+
**Level 2 — verify the plan before running any of it**
|
|
177
|
+
```python
|
|
178
|
+
result = toolkit.az.verify_chain({
|
|
179
|
+
"steps": [
|
|
180
|
+
{"id": "s1", "action_type": "execute_sql", "domain": "database_ops",
|
|
181
|
+
"action_description": "SELECT ssn, name FROM customers"},
|
|
182
|
+
{"id": "s2", "action_type": "send_email", "domain": "email_ops",
|
|
183
|
+
"action_description": "email the list to external-analytics@gmail.com"},
|
|
184
|
+
]
|
|
185
|
+
}, stop_on_block=True)
|
|
186
|
+
# → overall_verdict "SEMANTIC_BLOCK", blocked_at "s2",
|
|
187
|
+
# semantic_triggers [{"pattern_name": "structural_exfiltration", ...}]
|
|
188
|
+
|
|
189
|
+
# Response: {overall_verdict, blocked_at, steps[], semantic_triggers,
|
|
190
|
+
# final_state, session_state_updated}
|
|
191
|
+
# There is no top-level "verdict" — per-step verdicts live under steps[].
|
|
192
|
+
if result["overall_verdict"] != "SAFE":
|
|
193
|
+
# blocked_at names the step only when execution was actually stopped.
|
|
194
|
+
# On REVIEW_REQUIRED nothing was blocked, so it is null — find the step
|
|
195
|
+
# that needs attention in steps[] instead.
|
|
196
|
+
step = result["blocked_at"] or next(
|
|
197
|
+
(s["id"] for s in result["steps"] if s["verdict"] != "ALLOW"), None
|
|
198
|
+
)
|
|
199
|
+
abort(step)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`overall_verdict` is one of:
|
|
203
|
+
|
|
204
|
+
| Value | Meaning | `blocked_at` |
|
|
205
|
+
|---|---|---|
|
|
206
|
+
| `SAFE` | every step cleared | `null` |
|
|
207
|
+
| `BLOCKED` | a single step was blocked on its own merits | the step id |
|
|
208
|
+
| `SEMANTIC_BLOCK` | the steps are individually fine but **compose** into harm — check `semantic_triggers` (e.g. `structural_exfiltration`, `credential_exfiltration`, `recon_then_exfil`) | the step id |
|
|
209
|
+
| `REVIEW_REQUIRED` | a step needs evidence or human approval | `null` — nothing was blocked |
|
|
210
|
+
|
|
211
|
+
> **Describe the artefact, not just the operation.** Arcezia grounds its
|
|
212
|
+
> verdicts on concrete referents in the description — file paths, URLs,
|
|
213
|
+
> recipient addresses, credential and PII field names. `"SELECT ssn, name FROM
|
|
214
|
+
> customers"` names PII, so the read grounds as sensitive access and the chain
|
|
215
|
+
> above composes into `SEMANTIC_BLOCK`. `"SELECT email, name FROM customers"`
|
|
216
|
+
> names only column identifiers, so the same chain returns `REVIEW_REQUIRED`
|
|
217
|
+
> instead: still not `SAFE`, still not executable, but held for a human rather
|
|
218
|
+
> than positively identified as exfiltration.
|
|
219
|
+
>
|
|
220
|
+
> The rule this reflects: **a vague description degrades a verdict toward
|
|
221
|
+
> review — never toward approval.** Arcezia never allows what it could not
|
|
222
|
+
> verify, so imprecision costs you review latency, not safety. The framework
|
|
223
|
+
> adapters get this right automatically because they pass the real tool
|
|
224
|
+
> arguments; it is worth attention only when you hand-build chain manifests.
|
|
225
|
+
|
|
226
|
+
Chain steps do **not** inherit the session's capability envelope, so
|
|
227
|
+
`action_within_task_scope` stays unresolved and a chain will not reach `SAFE`
|
|
228
|
+
on the envelope alone. Ground it per step with an `evidence` dict (the key is
|
|
229
|
+
`evidence` — `agent_evidence` is ignored on chain steps):
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
{"id": "s1", "action_type": "execute_sql", "domain": "database_ops",
|
|
233
|
+
"action_description": "SELECT ssn, name FROM customers",
|
|
234
|
+
"evidence": {"action_within_task_scope": True}}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
`id` and `step_id` are accepted interchangeably. Note that `state_mutations`
|
|
238
|
+
may only *add* danger, never remove it: asserting a danger flag `True` is
|
|
239
|
+
accepted, asserting it `False` is rejected, and flags the engine derives for
|
|
240
|
+
itself (the `g_*` world-state namespace) are not caller-writable at all.
|
|
241
|
+
|
|
242
|
+
Audit after execution — did reality match the prediction?
|
|
243
|
+
```python
|
|
244
|
+
toolkit.az.verify_outcome(
|
|
245
|
+
action_type="execute_sql",
|
|
246
|
+
action_description="DELETE FROM orders WHERE test = true",
|
|
247
|
+
outcome={"rows_affected": 50000}, # what ACTUALLY happened
|
|
248
|
+
expected={"rows_affected": 1}, # what you intended
|
|
249
|
+
)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**Level 3 — ground the evidence.** Register a probe webhook so evidence is
|
|
253
|
+
`GROUNDED` rather than `CLAIMED`. Human intent can never be produced by a model,
|
|
254
|
+
so ground it explicitly:
|
|
255
|
+
```python
|
|
256
|
+
toolkit.az.authorize(user_token) # user_explicit_authorization
|
|
257
|
+
toolkit.az.authorize_production(prod_token) # production_explicit_authorization
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
These ground **different** constraints. Actions touching production generally
|
|
261
|
+
need both — `authorize()` alone will leave `production_explicit_authorization`
|
|
262
|
+
unresolved and the action stays in `REVIEW`.
|
|
263
|
+
|
|
264
|
+
> **Integrating over raw HTTP** (n8n, curl, another language)? Two things the
|
|
265
|
+
> SDK handles for you: the API is behind a WAF that rejects the default
|
|
266
|
+
> library agent strings (e.g. `Python-urllib/*`), so send an explicit
|
|
267
|
+
> `User-Agent` of your own; and the verify response field on the wire is
|
|
268
|
+
> `dc_score` — the SDK exposes it as `cert.precondition_score`.
|
|
269
|
+
|
|
270
|
+
Full guide: [arcezia.com/docs](https://arcezia.com/developer-docs)
|
|
271
|
+
|
|
272
|
+
## Development mode
|
|
273
|
+
|
|
274
|
+
Use an `ar_test_` key for local development — infrastructure constraints
|
|
275
|
+
(backup APIs, capability envelopes, CI gates) are relaxed so you are not blocked
|
|
276
|
+
by production infra that does not exist on your laptop:
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
az = arcezia.Arcezia(api_key="ar_test_...", task="...") # dev mode by default
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Development mode is only available on `ar_test_` keys and is re-checked
|
|
283
|
+
server-side. Live `ar_live_` keys are always pinned to production and cannot
|
|
284
|
+
point at `localhost`.
|
|
285
|
+
|
|
286
|
+
## Links
|
|
287
|
+
|
|
288
|
+
- [Developer documentation](https://arcezia.com/developer-docs)
|
|
289
|
+
- [Dashboard](https://app.arcezia.com)
|