consequence-gate 0.1.0__py3-none-any.whl
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.
- consequence_gate/__init__.py +3 -0
- consequence_gate/backtest/__init__.py +0 -0
- consequence_gate/backtest/harness.py +43 -0
- consequence_gate/backtest/reporter.py +20 -0
- consequence_gate/cli.py +75 -0
- consequence_gate/core/__init__.py +0 -0
- consequence_gate/core/circuit_breaker.py +56 -0
- consequence_gate/core/evaluator.py +27 -0
- consequence_gate/core/models.py +35 -0
- consequence_gate/integrations/__init__.py +0 -0
- consequence_gate/integrations/examples/run_langgraph.py +60 -0
- consequence_gate/integrations/examples/run_mcp_proxy.py +100 -0
- consequence_gate/integrations/langgraph_hook.py +197 -0
- consequence_gate/integrations/mcp_proxy.py +316 -0
- consequence_gate/integrations/strands_hook.py +224 -0
- consequence_gate/simulators/__init__.py +0 -0
- consequence_gate/simulators/communications.py +290 -0
- consequence_gate/simulators/database.py +165 -0
- consequence_gate/simulators/financial.py +102 -0
- consequence_gate-0.1.0.dist-info/METADATA +254 -0
- consequence_gate-0.1.0.dist-info/RECORD +24 -0
- consequence_gate-0.1.0.dist-info/WHEEL +5 -0
- consequence_gate-0.1.0.dist-info/entry_points.txt +2 -0
- consequence_gate-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: consequence-gate
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Speculative outcome-simulation layer for AI agent tool calls — predicts consequence (blast radius, irreversibility, velocity) before execution and steers agents toward safer alternatives.
|
|
5
|
+
Author-email: Anandkrishnan Shnn <anandkrshnn@gmail.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/anandkrshnn-ai/consequence-gate
|
|
8
|
+
Project-URL: Documentation, https://github.com/anandkrshnn-ai/consequence-gate#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/anandkrshnn-ai/consequence-gate
|
|
10
|
+
Project-URL: Issues, https://github.com/anandkrshnn-ai/consequence-gate/issues
|
|
11
|
+
Keywords: ai-agents,ai-safety,ai-governance,langchain,langgraph,mcp,agent-security,runtime-governance
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
Requires-Dist: langchain>=0.3.0
|
|
25
|
+
Requires-Dist: langchain-agents>=0.3.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
29
|
+
Requires-Dist: black>=23.0; extra == "dev"
|
|
30
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
31
|
+
Provides-Extra: strands
|
|
32
|
+
Requires-Dist: strands-agents>=0.1.0; extra == "strands"
|
|
33
|
+
Provides-Extra: mcp
|
|
34
|
+
Provides-Extra: langgraph
|
|
35
|
+
Requires-Dist: langgraph>=0.2.0; extra == "langgraph"
|
|
36
|
+
|
|
37
|
+
# consequence-gate
|
|
38
|
+
|
|
39
|
+
A speculative outcome-simulation layer for AI agent tool calls. It sits
|
|
40
|
+
**upstream** of static runtime access gates (AgentWall, AWS Strands
|
|
41
|
+
`BeforeToolCallEvent`, MCP proxies, Prisma AIRS) and asks a different
|
|
42
|
+
question than they do.
|
|
43
|
+
|
|
44
|
+
Static gates ask: *does this call match an allowed pattern?*
|
|
45
|
+
`consequence-gate` asks: *what will this call actually do, and is that
|
|
46
|
+
outcome safe?*
|
|
47
|
+
|
|
48
|
+
## Why this exists
|
|
49
|
+
|
|
50
|
+
Static runtime gates are fast (sub-millisecond) and effective at schema
|
|
51
|
+
validation, RBAC, and pattern matching -- but a schema-valid,
|
|
52
|
+
policy-compliant call can still be consequence-catastrophic. A
|
|
53
|
+
`process_claim(amount=50000)` call can pass every static check while
|
|
54
|
+
pushing an account over its daily velocity limit via an irreversible
|
|
55
|
+
instant transfer. `consequence-gate` projects the *outcome* of a call
|
|
56
|
+
(balance deltas, row-count blast radius, FK cascade depth,
|
|
57
|
+
irreversibility) before the call reaches your existing static gate, and
|
|
58
|
+
either passes it through, asks a human, denies it outright, or steers
|
|
59
|
+
the agent toward a pre-vetted safer alternative.
|
|
60
|
+
|
|
61
|
+
This is explicitly **not** a replacement for AgentWall / Strands / MCP
|
|
62
|
+
proxies -- it's a prediction layer that runs before them, in the same
|
|
63
|
+
pipeline.
|
|
64
|
+
|
|
65
|
+
## Core contracts
|
|
66
|
+
|
|
67
|
+
- **No silent argument mutation.** Steering returns structured guidance
|
|
68
|
+
and a suggested alternative call; the agent (or a human) still has to
|
|
69
|
+
commit to it. This preserves the audit property that every executed
|
|
70
|
+
call was one the agent explicitly chose.
|
|
71
|
+
- **Idempotency keys are derived from the transaction's own natural key**
|
|
72
|
+
(e.g. `claim_id`, or `table + filter hash`), never a fresh random token
|
|
73
|
+
per retry -- otherwise a lost-response retry looks like a brand-new
|
|
74
|
+
transaction instead of a duplicate.
|
|
75
|
+
- **Hard retry cap on STEER.** Regardless of guidance quality, retries
|
|
76
|
+
are capped (default: 2) before forcing escalation to a human, as a
|
|
77
|
+
backstop against loop-thrashing.
|
|
78
|
+
- **Confidence-gated escalation.** Low-confidence projections route to
|
|
79
|
+
`ASK`, never to a confident-looking `ALLOW` or `DENY` -- an
|
|
80
|
+
unfounded heuristic is worse than admitting uncertainty.
|
|
81
|
+
|
|
82
|
+
## Modules
|
|
83
|
+
|
|
84
|
+
- `consequence_gate.simulators.financial` -- **functional**: disbursement / claim / refund
|
|
85
|
+
velocity and irreversibility modeling.
|
|
86
|
+
- `consequence_gate.simulators.database` -- **functional**: row-count blast radius via the
|
|
87
|
+
DB's own query planner (`EXPLAIN`, not hardcoded selectivity constants)
|
|
88
|
+
and recursive `ON DELETE CASCADE` graph walking.
|
|
89
|
+
- `consequence_gate.simulators.communications` -- **functional**: outbound email/SMS/notification
|
|
90
|
+
blast radius, unsubscribe suppression compliance, canary cohort analysis, sender reputation impact.
|
|
91
|
+
- `consequence_gate.core` -- shared models, the confidence/threshold
|
|
92
|
+
evaluator, and the idempotency-locked circuit breaker.
|
|
93
|
+
- `consequence_gate.integrations.strands_hook` -- **functional**: AWS Strands
|
|
94
|
+
`BeforeToolCallEvent` adapter with full `ALLOW`/`DENY`/`ASK`/`STEER` lifecycle.
|
|
95
|
+
- `consequence_gate.integrations.mcp_proxy` -- **functional**: MCP stdio proxy
|
|
96
|
+
intercepting `tools/call` requests, returning JSON-RPC errors or `isError=true` tool results.
|
|
97
|
+
- `consequence_gate.integrations.langgraph_hook` -- **functional**: LangGraph middleware
|
|
98
|
+
(`@wrap_tool_call`) intercepting tool execution with full `ALLOW`/`DENY`/`ASK`/`STEER` lifecycle.
|
|
99
|
+
- `consequence_gate.backtest` -- offline JSONL trace replay harness and
|
|
100
|
+
four-quadrant FP/FN/TN report generator, for evaluating this layer
|
|
101
|
+
against historical execution logs with zero production integration.
|
|
102
|
+
|
|
103
|
+
## Quickstart
|
|
104
|
+
|
|
105
|
+
### Installation
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
pip install -e ".[dev]"
|
|
109
|
+
pytest
|
|
110
|
+
python examples/run_backtest_demo.py
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### AWS Strands Integration
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from consequence_gate.integrations.strands_hook import create_financial_gate_hook
|
|
117
|
+
from strands.agents import Agent
|
|
118
|
+
|
|
119
|
+
hook = create_financial_gate_hook(
|
|
120
|
+
daily_tier_limit_inr=25000.0,
|
|
121
|
+
instant_wire_threshold=10000.0,
|
|
122
|
+
max_retries=2,
|
|
123
|
+
context_provider=lambda event: {
|
|
124
|
+
"account_rolling_24h_spend": get_current_spend(event),
|
|
125
|
+
"kyc_verified": is_kyc_verified(event),
|
|
126
|
+
},
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
agent = Agent(hooks=[hook])
|
|
130
|
+
response = agent("Process this claim for 50,000 INR")
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### MCP Proxy Integration
|
|
134
|
+
|
|
135
|
+
Run as a standalone proxy in front of any MCP server:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# Financial disbursement gate
|
|
139
|
+
python -m consequence_gate.integrations.examples.run_mcp_proxy financial \\
|
|
140
|
+
--downstream-command "npx -y @modelcontextprotocol/server-postgres postgresql://localhost/mydb" \\
|
|
141
|
+
--daily-tier-limit 25000 \\
|
|
142
|
+
--instant-wire-threshold 10000
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Configure in Claude Desktop / Cursor / Windsurf:
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"mcpServers": {
|
|
150
|
+
"my-consequence-gate": {
|
|
151
|
+
"command": "python",
|
|
152
|
+
"args": ["-m", "consequence_gate.integrations.examples.run_mcp_proxy", "financial", "--downstream-command", "npx -y @modelcontextprotocol/server-postgres postgresql://localhost/mydb"]
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### LangGraph Integration
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
from langchain.agents import create_agent
|
|
162
|
+
from consequence_gate.integrations.langgraph_hook import create_financial_gate_middleware
|
|
163
|
+
|
|
164
|
+
middleware = create_financial_gate_middleware(
|
|
165
|
+
daily_tier_limit_inr=25000.0,
|
|
166
|
+
instant_wire_threshold=10000.0,
|
|
167
|
+
max_retries=2,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
agent = create_agent(
|
|
171
|
+
model="claude-sonnet-4",
|
|
172
|
+
tools=[my_tool],
|
|
173
|
+
middleware=[middleware],
|
|
174
|
+
)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Run the example:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
python -m consequence_gate.integrations.examples.run_langgraph
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Database Deletion Gate (Strands)
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from consequence_gate.integrations.strands_hook import create_database_gate_hook
|
|
187
|
+
|
|
188
|
+
hook = create_database_gate_hook(
|
|
189
|
+
max_autonomous_delete_rows=100,
|
|
190
|
+
db_conn=get_db_connection(),
|
|
191
|
+
max_retries=2,
|
|
192
|
+
context_provider=lambda event: {
|
|
193
|
+
"table_metadata": get_table_metadata(event),
|
|
194
|
+
},
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
agent = Agent(hooks=[hook])
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Communications Blast Gate (Strands)
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
from consequence_gate.integrations.strands_hook import create_communication_gate_hook
|
|
204
|
+
|
|
205
|
+
hook = create_communication_gate_hook(
|
|
206
|
+
max_autonomous_recipients=10000,
|
|
207
|
+
canary_min_size=100,
|
|
208
|
+
canary_max_bounce_rate=0.05,
|
|
209
|
+
canary_max_complaint_rate=0.01,
|
|
210
|
+
context_provider=lambda event: {
|
|
211
|
+
"segment_counts": get_segment_counts(event),
|
|
212
|
+
"recent_unsubscribes": get_recent_unsubscribes(event),
|
|
213
|
+
"historical_bounce_rate": 0.02,
|
|
214
|
+
"historical_complaint_rate": 0.005,
|
|
215
|
+
},
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
agent = Agent(hooks=[hook])
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Decision Matrix
|
|
222
|
+
|
|
223
|
+
| Decision | Strands | MCP | LangGraph |
|
|
224
|
+
|----------|---------|-----|-----------|
|
|
225
|
+
| `ALLOW` | Executes normally | Forwarded to downstream MCP server | Tool executes via handler(request) |
|
|
226
|
+
| `DENY` | `BLOCKED: <reason>` | JSON-RPC error (code=-32603) | Raises `ValueError("BLOCKED: ...")` |
|
|
227
|
+
| `ASK` | `ESCALATION_REQUIRED: <reason>` | `isError=true` tool result | Raises `ValueError("ESCALATION_REQUIRED: ...")` |
|
|
228
|
+
| `STEER` | `STEER_GUIDANCE: <guidance>\nSuggested alternative...` | `isError=true` + guidance | `ToolMessage(content="STEER_GUIDANCE: ...", status="error")` |
|
|
229
|
+
|
|
230
|
+
## Backtest Workflow
|
|
231
|
+
|
|
232
|
+
Before deploying to production, run an offline backtest against historical
|
|
233
|
+
execution traces:
|
|
234
|
+
|
|
235
|
+
1. Export 1,000-5,000 tool-call traces as JSONL (see `examples/backtest_sample_traces.jsonl`)
|
|
236
|
+
2. Run `python examples/run_backtest_demo.py` against your traces
|
|
237
|
+
3. Review the four-quadrant breakdown:
|
|
238
|
+
- True Negative: correctly allowed benign operations
|
|
239
|
+
- False Negative Caught: schema-valid calls that would have breached limits
|
|
240
|
+
- False Positive Relieved: over-blocking that the simulator would have avoided
|
|
241
|
+
- Steer Recovery Rate: percentage of blocked turns that could have completed via guidance
|
|
242
|
+
|
|
243
|
+
## Status
|
|
244
|
+
|
|
245
|
+
- **Financial simulator**: functional with unit tests
|
|
246
|
+
- **Database simulator**: functional with unit tests (EXPLAIN-based row estimation, recursive FK cascade walk)
|
|
247
|
+
- **Communications simulator**: functional with unit tests (blast radius, unsubscribe compliance, canary cohorts, reputation impact)
|
|
248
|
+
- **Strands integration**: functional with unit tests (full `ALLOW`/`DENY`/`ASK`/`STEER` lifecycle)
|
|
249
|
+
- **MCP integration**: functional with unit tests (stdio transport, JSON-RPC error handling)
|
|
250
|
+
- **LangGraph integration**: functional with unit tests (`@wrap_tool_call` middleware pattern)
|
|
251
|
+
|
|
252
|
+
## License
|
|
253
|
+
|
|
254
|
+
Apache-2.0
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
consequence_gate/__init__.py,sha256=capa7egh8wyDQuuVT-KewzvT9Kl6bf_CYTlxe6i9yh4,112
|
|
2
|
+
consequence_gate/cli.py,sha256=Asv4pJv9EEzZPMIJeCpn2RNg4ZTfk2oCCN3vT6x7mqU,3021
|
|
3
|
+
consequence_gate/backtest/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
consequence_gate/backtest/harness.py,sha256=cukzr9XdP2ZvfI_5WJZgHijS9tYXYP265uhimCyBlcg,1668
|
|
5
|
+
consequence_gate/backtest/reporter.py,sha256=6t06MCLKAXQNX81_oSPagTDAO2icSeJNuVxLg7-fOpw,796
|
|
6
|
+
consequence_gate/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
consequence_gate/core/circuit_breaker.py,sha256=D-xftZPMYMgKnIGjJeMZNcaba3ZjZhtHK-e_EBnUJ5s,2242
|
|
8
|
+
consequence_gate/core/evaluator.py,sha256=Zd65nydgN9v36cLgVPGl7RUho0175hFN5XqWAjtHvqc,1050
|
|
9
|
+
consequence_gate/core/models.py,sha256=eoxOFZiUaIFgoGLW1z_3I13vNXkIJttns_glC3j0b0g,1124
|
|
10
|
+
consequence_gate/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
consequence_gate/integrations/langgraph_hook.py,sha256=FRtLaFfUSBGlnFZ79Z60BoihsJDQ_4xGE7sCF4CvoMg,7577
|
|
12
|
+
consequence_gate/integrations/mcp_proxy.py,sha256=ASKc0LgAC6avPZd_0IPTzUsyQ4hdfJovd2C5ee0c-40,12247
|
|
13
|
+
consequence_gate/integrations/strands_hook.py,sha256=3vKYILbDJgSSwFAlKkGE1yk7VYkVl3RmVPR0pGZ8xOU,9106
|
|
14
|
+
consequence_gate/integrations/examples/run_langgraph.py,sha256=T4bxiyhz1QDxggOEWbZEe22eyYYg_9RLd2BbaZvUwyo,2002
|
|
15
|
+
consequence_gate/integrations/examples/run_mcp_proxy.py,sha256=NQVfnpyqmeZ-uMP38j65WBc_xQ3N-yHeMze3mG2P4KQ,4217
|
|
16
|
+
consequence_gate/simulators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
consequence_gate/simulators/communications.py,sha256=70-QRo3dcXOxmz0T8wxBi6FOVTdHThCgDs-FftntfNo,12312
|
|
18
|
+
consequence_gate/simulators/database.py,sha256=81OAgZ8tnmW9NZehOkgbjPyGOdtnZBCYNfkAvB7Loww,7245
|
|
19
|
+
consequence_gate/simulators/financial.py,sha256=AWrW17NWDqdBgnZ5_IHbffGKQR5SV_Vtx38kXUghamE,4890
|
|
20
|
+
consequence_gate-0.1.0.dist-info/METADATA,sha256=zQ7Mw7H6qF-5brB0kJrHR8uZS-DIAanRF4f73v0AUyU,10290
|
|
21
|
+
consequence_gate-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
22
|
+
consequence_gate-0.1.0.dist-info/entry_points.txt,sha256=Xh5eptmtjTupU4XYaiEmp0Levcyb1sZJDQ7o6QyYbhQ,63
|
|
23
|
+
consequence_gate-0.1.0.dist-info/top_level.txt,sha256=9a5mtcWJWPK2BVse-gSSwKFGyFzPLL77S3LsQkfCZ-g,17
|
|
24
|
+
consequence_gate-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
consequence_gate
|