wardproof 0.3.0__tar.gz → 0.3.2__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.
Files changed (66) hide show
  1. {wardproof-0.3.0 → wardproof-0.3.2}/PKG-INFO +33 -11
  2. {wardproof-0.3.0 → wardproof-0.3.2}/README.md +28 -10
  3. {wardproof-0.3.0 → wardproof-0.3.2}/pyproject.toml +7 -1
  4. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/__init__.py +1 -1
  5. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/cli.py +55 -0
  6. {wardproof-0.3.0 → wardproof-0.3.2}/.gitignore +0 -0
  7. {wardproof-0.3.0 → wardproof-0.3.2}/CONTRIBUTING.md +0 -0
  8. {wardproof-0.3.0 → wardproof-0.3.2}/LICENSE +0 -0
  9. {wardproof-0.3.0 → wardproof-0.3.2}/SECURITY.md +0 -0
  10. {wardproof-0.3.0 → wardproof-0.3.2}/THREAT_MODEL.md +0 -0
  11. {wardproof-0.3.0 → wardproof-0.3.2}/benchmarks/README.md +0 -0
  12. {wardproof-0.3.0 → wardproof-0.3.2}/benchmarks/corpus.jsonl +0 -0
  13. {wardproof-0.3.0 → wardproof-0.3.2}/benchmarks/external/README.md +0 -0
  14. {wardproof-0.3.0 → wardproof-0.3.2}/benchmarks/external/__init__.py +0 -0
  15. {wardproof-0.3.0 → wardproof-0.3.2}/benchmarks/external/_screen.py +0 -0
  16. {wardproof-0.3.0 → wardproof-0.3.2}/benchmarks/external/agentdojo.py +0 -0
  17. {wardproof-0.3.0 → wardproof-0.3.2}/benchmarks/external/fetch_data.py +0 -0
  18. {wardproof-0.3.0 → wardproof-0.3.2}/benchmarks/external/injecagent.py +0 -0
  19. {wardproof-0.3.0 → wardproof-0.3.2}/benchmarks/heldout.py +0 -0
  20. {wardproof-0.3.0 → wardproof-0.3.2}/benchmarks/latency.py +0 -0
  21. {wardproof-0.3.0 → wardproof-0.3.2}/benchmarks/run_benchmark.py +0 -0
  22. {wardproof-0.3.0 → wardproof-0.3.2}/examples/agent_to_agent_transfer.py +0 -0
  23. {wardproof-0.3.0 → wardproof-0.3.2}/examples/integrations/README.md +0 -0
  24. {wardproof-0.3.0 → wardproof-0.3.2}/examples/integrations/agentkit_guarded.py +0 -0
  25. {wardproof-0.3.0 → wardproof-0.3.2}/examples/integrations/anthropic_tools_guarded.py +0 -0
  26. {wardproof-0.3.0 → wardproof-0.3.2}/examples/integrations/crewai_guarded.py +0 -0
  27. {wardproof-0.3.0 → wardproof-0.3.2}/examples/integrations/langgraph_guarded.py +0 -0
  28. {wardproof-0.3.0 → wardproof-0.3.2}/examples/integrations/mcp_guarded.py +0 -0
  29. {wardproof-0.3.0 → wardproof-0.3.2}/examples/integrations/openai_tools_guarded.py +0 -0
  30. {wardproof-0.3.0 → wardproof-0.3.2}/examples/integrations/skills_guard.py +0 -0
  31. {wardproof-0.3.0 → wardproof-0.3.2}/examples/integrations/venice_guarded.py +0 -0
  32. {wardproof-0.3.0 → wardproof-0.3.2}/examples/morse_injection_blocked_at_action.py +0 -0
  33. {wardproof-0.3.0 → wardproof-0.3.2}/examples/protect_defi_agent.py +0 -0
  34. {wardproof-0.3.0 → wardproof-0.3.2}/examples/protect_mcp_agent.py +0 -0
  35. {wardproof-0.3.0 → wardproof-0.3.2}/examples/protect_rag_app.py +0 -0
  36. {wardproof-0.3.0 → wardproof-0.3.2}/examples/protect_x402_payments.py +0 -0
  37. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/agents/__init__.py +0 -0
  38. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/agents/base.py +0 -0
  39. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/agents/detector.py +0 -0
  40. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/agents/responder.py +0 -0
  41. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/agents/verifier.py +0 -0
  42. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/audit/__init__.py +0 -0
  43. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/audit/ledger.py +0 -0
  44. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/audit/stix.py +0 -0
  45. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/config.py +0 -0
  46. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/guardrails/__init__.py +0 -0
  47. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/guardrails/_normalize.py +0 -0
  48. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/guardrails/base.py +0 -0
  49. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/guardrails/mcp_guard.py +0 -0
  50. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/guardrails/memory_poisoning.py +0 -0
  51. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/guardrails/prompt_injection.py +0 -0
  52. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/guardrails/tool_misuse.py +0 -0
  53. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/guardrails/transfer.py +0 -0
  54. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/guardrails/x402_payment.py +0 -0
  55. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/llm/__init__.py +0 -0
  56. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/llm/base.py +0 -0
  57. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/llm/null.py +0 -0
  58. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/llm/ollama_client.py +0 -0
  59. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/orchestration/__init__.py +0 -0
  60. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/orchestration/engine.py +0 -0
  61. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/orchestration/factory.py +0 -0
  62. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/sandbox/__init__.py +0 -0
  63. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/sandbox/executor.py +0 -0
  64. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/sandbox/permissions.py +0 -0
  65. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/schema.py +0 -0
  66. {wardproof-0.3.0 → wardproof-0.3.2}/wardproof/standards.py +0 -0
@@ -1,7 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wardproof
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: Local-first, verifiable defensive AI agent swarms that protect other AI agent systems.
5
+ Project-URL: Homepage, https://wardproof.xyz
6
+ Project-URL: Repository, https://github.com/Impossible-Mission-Force/wardproof
7
+ Project-URL: Documentation, https://github.com/Impossible-Mission-Force/wardproof#readme
8
+ Project-URL: Issues, https://github.com/Impossible-Mission-Force/wardproof/issues
5
9
  Author: Wardproof contributors
6
10
  License-Expression: MIT
7
11
  License-File: LICENSE
@@ -57,7 +61,7 @@ decision.
57
61
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/Impossible-Mission-Force/wardproof/blob/main/LICENSE)
58
62
  [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
59
63
 
60
- ![Wardproof screening x402 payments: a legitimate payment is allowed while an attacker redirect, a replayed payment, and a prompt injection in the 402 body are all blocked and written to a tamper-evident ledger.](assets/wardproof-x402-demo.gif)
64
+ ![Wardproof screening x402 payments: a legitimate payment is allowed while an attacker redirect, a replayed payment, and a prompt injection in the 402 body are all blocked and written to a tamper-evident ledger.](https://raw.githubusercontent.com/Impossible-Mission-Force/wardproof/main/assets/wardproof-x402-demo.gif)
61
65
 
62
66
  Wardproof is a small framework for building swarms of *defensive* agents that
63
67
  sit in front of your *other* AI systems (RAG pipelines, tool-using agents,
@@ -70,7 +74,7 @@ It is deliberately **small, transparent, and forkable**. The security core has
70
74
  **zero third-party dependencies** and runs **fully offline**, with a local
71
75
  model via Ollama, or with no model at all.
72
76
 
73
- > **Status: v0.3.0.** The deterministic core is built, tested, and benchmarked
77
+ > **Status: v0.3.1.** The deterministic core is built, tested, and benchmarked
74
78
  > (see [Benchmark](#benchmark)), and ships dedicated guards for x402 agent
75
79
  > payments, on-chain transfers, MCP tool calls, and skill/tool definitions, a
76
80
  > controls-to-standards map (OWASP Agentic Top 10, OWASP LLM 2025, MITRE ATLAS,
@@ -79,8 +83,8 @@ model via Ollama, or with no model at all.
79
83
  > examples for OpenAI and Anthropic tool calling, CrewAI, LangGraph, MCP,
80
84
  > Coinbase AgentKit, and Venice AI. It is
81
85
  > deployable today as a screening and audit layer, designed to run as defence in
82
- > depth within the scope set out in [`THREAT_MODEL.md`](THREAT_MODEL.md) and
83
- > [`SECURITY.md`](SECURITY.md).
86
+ > depth within the scope set out in [`THREAT_MODEL.md`](https://github.com/Impossible-Mission-Force/wardproof/blob/main/THREAT_MODEL.md) and
87
+ > [`SECURITY.md`](https://github.com/Impossible-Mission-Force/wardproof/blob/main/SECURITY.md).
84
88
 
85
89
  ---
86
90
 
@@ -131,7 +135,7 @@ different stance:
131
135
  OpenAI and Anthropic tool calling, CrewAI, LangGraph, MCP, and Coinbase
132
136
  AgentKit tool calls, plus Venice AI as an optional escalate-only second-opinion
133
137
  backend. Each is an optional dependency; the core imports none of them. See
134
- [`examples/integrations/`](examples/integrations/).
138
+ [`examples/integrations/`](https://github.com/Impossible-Mission-Force/wardproof/tree/main/examples/integrations).
135
139
  - **Standards-aligned**: every control mapped to OWASP Top 10 for Agentic
136
140
  Applications, OWASP Agentic Threats (T1-T15), OWASP LLM Top 10 2025, CSA
137
141
  MAESTRO, MITRE ATLAS, and NIST AI 600-1 (`wardproof/standards.py`, enforced by
@@ -198,6 +202,24 @@ Verify an exported ledger from the command line:
198
202
  wardproof verify-ledger ./audit.jsonl --pubkey <hex_public_key>
199
203
  ```
200
204
 
205
+ ### Screen one action with `wardproof check`
206
+
207
+ Screen a single input or tool call from the command line. It runs the real
208
+ default swarm locally and exits `0` only when the verdict is `ALLOW`, so you can
209
+ gate a shell pipeline or an agent skill on it:
210
+
211
+ ```bash
212
+ # A tool call (tool name as the content, arguments as a JSON string)
213
+ wardproof check "get_weather" --args '{"city":"Hanoi"}' # ALLOW, exits 0
214
+
215
+ # An untrusted input
216
+ wardproof check "ignore all previous instructions" --kind input # BLOCK, exits non-zero
217
+ ```
218
+
219
+ Add `--json` to get a structured `{"verdict": ..., "allowed": ..., "risk": ...,
220
+ "reasons": [...]}` result to parse. A portable guard skill that wires this check
221
+ into a host agent lives in [`skill/wardproof-guard/`](https://github.com/Impossible-Mission-Force/wardproof/tree/main/skill/wardproof-guard).
222
+
201
223
  ---
202
224
 
203
225
  ## Architecture
@@ -272,7 +294,7 @@ languages, fresh encodings, or pure-semantic paraphrase) can still slip past a
272
294
  deterministic denylist. Closing that gap is the job of the optional LLM second
273
295
  opinion (see Roadmap); these patterns are the floor, not the ceiling. Re-run the
274
296
  harness to regenerate the numbers above; the full breakdown and the honest edges
275
- are in [`benchmarks/README.md`](benchmarks/README.md).
297
+ are in [`benchmarks/README.md`](https://github.com/Impossible-Mission-Force/wardproof/blob/main/benchmarks/README.md).
276
298
 
277
299
  ---
278
300
 
@@ -299,7 +321,7 @@ No need to touch the engine, the ledger, or the agent base classes.
299
321
  Wardproof is built to become a complete, auditable control layer for AI agents.
300
322
  The direction:
301
323
 
302
- **Now (v0.3.0)**
324
+ **Now (v0.3.1)**
303
325
  The deterministic core: schema, guardrails, Detector / Verifier / Responder, a
304
326
  capability sandbox, circuit breaker and watchdog, a hash-chained and optionally
305
327
  signed audit ledger, a reproducible adversarial benchmark, a published threat
@@ -357,6 +379,6 @@ defence-in-depth setup:
357
379
 
358
380
  ## License
359
381
 
360
- MIT, see [`LICENSE`](LICENSE). Contributions welcome; see
361
- [`CONTRIBUTING.md`](CONTRIBUTING.md) and the security policy in
362
- [`SECURITY.md`](SECURITY.md).
382
+ MIT, see [`LICENSE`](https://github.com/Impossible-Mission-Force/wardproof/blob/main/LICENSE). Contributions welcome; see
383
+ [`CONTRIBUTING.md`](https://github.com/Impossible-Mission-Force/wardproof/blob/main/CONTRIBUTING.md) and the security policy in
384
+ [`SECURITY.md`](https://github.com/Impossible-Mission-Force/wardproof/blob/main/SECURITY.md).
@@ -11,7 +11,7 @@ decision.
11
11
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/Impossible-Mission-Force/wardproof/blob/main/LICENSE)
12
12
  [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
13
13
 
14
- ![Wardproof screening x402 payments: a legitimate payment is allowed while an attacker redirect, a replayed payment, and a prompt injection in the 402 body are all blocked and written to a tamper-evident ledger.](assets/wardproof-x402-demo.gif)
14
+ ![Wardproof screening x402 payments: a legitimate payment is allowed while an attacker redirect, a replayed payment, and a prompt injection in the 402 body are all blocked and written to a tamper-evident ledger.](https://raw.githubusercontent.com/Impossible-Mission-Force/wardproof/main/assets/wardproof-x402-demo.gif)
15
15
 
16
16
  Wardproof is a small framework for building swarms of *defensive* agents that
17
17
  sit in front of your *other* AI systems (RAG pipelines, tool-using agents,
@@ -24,7 +24,7 @@ It is deliberately **small, transparent, and forkable**. The security core has
24
24
  **zero third-party dependencies** and runs **fully offline**, with a local
25
25
  model via Ollama, or with no model at all.
26
26
 
27
- > **Status: v0.3.0.** The deterministic core is built, tested, and benchmarked
27
+ > **Status: v0.3.1.** The deterministic core is built, tested, and benchmarked
28
28
  > (see [Benchmark](#benchmark)), and ships dedicated guards for x402 agent
29
29
  > payments, on-chain transfers, MCP tool calls, and skill/tool definitions, a
30
30
  > controls-to-standards map (OWASP Agentic Top 10, OWASP LLM 2025, MITRE ATLAS,
@@ -33,8 +33,8 @@ model via Ollama, or with no model at all.
33
33
  > examples for OpenAI and Anthropic tool calling, CrewAI, LangGraph, MCP,
34
34
  > Coinbase AgentKit, and Venice AI. It is
35
35
  > deployable today as a screening and audit layer, designed to run as defence in
36
- > depth within the scope set out in [`THREAT_MODEL.md`](THREAT_MODEL.md) and
37
- > [`SECURITY.md`](SECURITY.md).
36
+ > depth within the scope set out in [`THREAT_MODEL.md`](https://github.com/Impossible-Mission-Force/wardproof/blob/main/THREAT_MODEL.md) and
37
+ > [`SECURITY.md`](https://github.com/Impossible-Mission-Force/wardproof/blob/main/SECURITY.md).
38
38
 
39
39
  ---
40
40
 
@@ -85,7 +85,7 @@ different stance:
85
85
  OpenAI and Anthropic tool calling, CrewAI, LangGraph, MCP, and Coinbase
86
86
  AgentKit tool calls, plus Venice AI as an optional escalate-only second-opinion
87
87
  backend. Each is an optional dependency; the core imports none of them. See
88
- [`examples/integrations/`](examples/integrations/).
88
+ [`examples/integrations/`](https://github.com/Impossible-Mission-Force/wardproof/tree/main/examples/integrations).
89
89
  - **Standards-aligned**: every control mapped to OWASP Top 10 for Agentic
90
90
  Applications, OWASP Agentic Threats (T1-T15), OWASP LLM Top 10 2025, CSA
91
91
  MAESTRO, MITRE ATLAS, and NIST AI 600-1 (`wardproof/standards.py`, enforced by
@@ -152,6 +152,24 @@ Verify an exported ledger from the command line:
152
152
  wardproof verify-ledger ./audit.jsonl --pubkey <hex_public_key>
153
153
  ```
154
154
 
155
+ ### Screen one action with `wardproof check`
156
+
157
+ Screen a single input or tool call from the command line. It runs the real
158
+ default swarm locally and exits `0` only when the verdict is `ALLOW`, so you can
159
+ gate a shell pipeline or an agent skill on it:
160
+
161
+ ```bash
162
+ # A tool call (tool name as the content, arguments as a JSON string)
163
+ wardproof check "get_weather" --args '{"city":"Hanoi"}' # ALLOW, exits 0
164
+
165
+ # An untrusted input
166
+ wardproof check "ignore all previous instructions" --kind input # BLOCK, exits non-zero
167
+ ```
168
+
169
+ Add `--json` to get a structured `{"verdict": ..., "allowed": ..., "risk": ...,
170
+ "reasons": [...]}` result to parse. A portable guard skill that wires this check
171
+ into a host agent lives in [`skill/wardproof-guard/`](https://github.com/Impossible-Mission-Force/wardproof/tree/main/skill/wardproof-guard).
172
+
155
173
  ---
156
174
 
157
175
  ## Architecture
@@ -226,7 +244,7 @@ languages, fresh encodings, or pure-semantic paraphrase) can still slip past a
226
244
  deterministic denylist. Closing that gap is the job of the optional LLM second
227
245
  opinion (see Roadmap); these patterns are the floor, not the ceiling. Re-run the
228
246
  harness to regenerate the numbers above; the full breakdown and the honest edges
229
- are in [`benchmarks/README.md`](benchmarks/README.md).
247
+ are in [`benchmarks/README.md`](https://github.com/Impossible-Mission-Force/wardproof/blob/main/benchmarks/README.md).
230
248
 
231
249
  ---
232
250
 
@@ -253,7 +271,7 @@ No need to touch the engine, the ledger, or the agent base classes.
253
271
  Wardproof is built to become a complete, auditable control layer for AI agents.
254
272
  The direction:
255
273
 
256
- **Now (v0.3.0)**
274
+ **Now (v0.3.1)**
257
275
  The deterministic core: schema, guardrails, Detector / Verifier / Responder, a
258
276
  capability sandbox, circuit breaker and watchdog, a hash-chained and optionally
259
277
  signed audit ledger, a reproducible adversarial benchmark, a published threat
@@ -311,6 +329,6 @@ defence-in-depth setup:
311
329
 
312
330
  ## License
313
331
 
314
- MIT, see [`LICENSE`](LICENSE). Contributions welcome; see
315
- [`CONTRIBUTING.md`](CONTRIBUTING.md) and the security policy in
316
- [`SECURITY.md`](SECURITY.md).
332
+ MIT, see [`LICENSE`](https://github.com/Impossible-Mission-Force/wardproof/blob/main/LICENSE). Contributions welcome; see
333
+ [`CONTRIBUTING.md`](https://github.com/Impossible-Mission-Force/wardproof/blob/main/CONTRIBUTING.md) and the security policy in
334
+ [`SECURITY.md`](https://github.com/Impossible-Mission-Force/wardproof/blob/main/SECURITY.md).
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "wardproof"
7
- version = "0.3.0"
7
+ version = "0.3.2"
8
8
  description = "Local-first, verifiable defensive AI agent swarms that protect other AI agent systems."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -13,6 +13,12 @@ keywords = ["ai-security", "prompt-injection", "agents", "guardrails", "local-fi
13
13
  authors = [{ name = "Wardproof contributors" }]
14
14
  dependencies = [] # core has ZERO third-party deps on purpose (small trusted computing base)
15
15
 
16
+ [project.urls]
17
+ Homepage = "https://wardproof.xyz"
18
+ Repository = "https://github.com/Impossible-Mission-Force/wardproof"
19
+ Documentation = "https://github.com/Impossible-Mission-Force/wardproof#readme"
20
+ Issues = "https://github.com/Impossible-Mission-Force/wardproof/issues"
21
+
16
22
  [project.optional-dependencies]
17
23
  ollama = ["httpx>=0.27"]
18
24
  crypto = ["cryptography>=42"]
@@ -16,7 +16,7 @@ from wardproof.sandbox.executor import SandboxExecutor, ToolRegistry
16
16
  from wardproof.sandbox.permissions import PermissionBroker, ToolGrant
17
17
  from wardproof.schema import Decision, Event, Finding, Severity, Verdict
18
18
 
19
- __version__ = "0.3.0"
19
+ __version__ = "0.3.2"
20
20
  __all__ = [
21
21
  "Event",
22
22
  "Decision",
@@ -9,6 +9,8 @@ from pathlib import Path
9
9
 
10
10
  from wardproof.audit.ledger import AuditEntry, AuditLedger
11
11
  from wardproof.audit.stix import to_stix_bundle
12
+ from wardproof.orchestration.factory import build_default_swarm
13
+ from wardproof.schema import Event, Verdict
12
14
 
13
15
 
14
16
  def _load_entries(path: str) -> list[AuditEntry]:
@@ -39,6 +41,47 @@ def _export_stix(path: str, out: str | None) -> int:
39
41
  return 0
40
42
 
41
43
 
44
+ def _check(
45
+ kind: str,
46
+ content: str,
47
+ source: str,
48
+ args_json: str | None,
49
+ as_json: bool,
50
+ ) -> int:
51
+ metadata: dict = {}
52
+ if args_json:
53
+ try:
54
+ metadata["args"] = json.loads(args_json)
55
+ except json.JSONDecodeError:
56
+ metadata["args"] = args_json
57
+ swarm = build_default_swarm()
58
+ out = swarm.handle(Event(kind=kind, source=source, content=content, metadata=metadata))
59
+ seen: set[str] = set()
60
+ reasons: list[str] = []
61
+ for d in (out.detector, out.verifier):
62
+ for f in d.findings:
63
+ if f.triggered and f.reason and f.reason not in seen:
64
+ seen.add(f.reason)
65
+ reasons.append(f.reason)
66
+ if as_json:
67
+ print(
68
+ json.dumps(
69
+ {
70
+ "verdict": out.verdict.value,
71
+ "allowed": out.verdict is Verdict.ALLOW,
72
+ "risk": round(out.risk, 3),
73
+ "reasons": reasons,
74
+ }
75
+ )
76
+ )
77
+ else:
78
+ print(out.verdict.value.upper())
79
+ if reasons:
80
+ print(" " + "; ".join(reasons))
81
+ # exit 0 only when the action is allowed, so shells and skills can gate on it
82
+ return 0 if out.verdict is Verdict.ALLOW else 2
83
+
84
+
42
85
  def main(argv: list[str] | None = None) -> int:
43
86
  parser = argparse.ArgumentParser(prog="wardproof")
44
87
  sub = parser.add_subparsers(dest="cmd", required=True)
@@ -48,11 +91,23 @@ def main(argv: list[str] | None = None) -> int:
48
91
  sp = sub.add_parser("export-stix", help="export a JSONL audit ledger as a STIX 2.1 bundle")
49
92
  sp.add_argument("path")
50
93
  sp.add_argument("--out", default=None, help="write to a file instead of stdout")
94
+ cp = sub.add_parser("check", help="screen one input or tool call and print its verdict")
95
+ cp.add_argument("content", help="the input text, or the tool name for a tool call")
96
+ cp.add_argument(
97
+ "--kind",
98
+ default="tool_call",
99
+ help="event kind: tool_call (default) or input",
100
+ )
101
+ cp.add_argument("--source", default="agent", help="who originated the event")
102
+ cp.add_argument("--args", default=None, help="tool-call arguments as a JSON string")
103
+ cp.add_argument("--json", action="store_true", help="print a JSON object instead of text")
51
104
  args = parser.parse_args(argv)
52
105
  if args.cmd == "verify-ledger":
53
106
  return _verify_file(args.path, args.pubkey)
54
107
  if args.cmd == "export-stix":
55
108
  return _export_stix(args.path, args.out)
109
+ if args.cmd == "check":
110
+ return _check(args.kind, args.content, args.source, args.args, args.json)
56
111
  return 1
57
112
 
58
113
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes