aegis-kernel 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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sneh Gabani
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,69 @@
1
+ Metadata-Version: 2.4
2
+ Name: aegis-kernel
3
+ Version: 1.0.0
4
+ Summary: Aegis Invariant Kernel: Deterministic Tool-Call Safety Clearance Gateway for AI Agents
5
+ Author-email: Sneh Gabani <sneh@aegis-kernel.dev>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://aegis-kernel.dev
8
+ Project-URL: Repository, https://github.com/Snehgabani/aegis-kernel
9
+ Keywords: ai-safety,agentic-ai,langchain,crewai,autogen,mcp,security,invariants
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.9
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Dynamic: license-file
16
+
17
+ # Aegis Invariant Kernel for Python
18
+
19
+ > **Deterministic Tool-Call Safety Gateway for Autonomous AI Agents**
20
+ > *Sub-0.1ms Latency • Zero External Dependencies • Zero Network Egress*
21
+
22
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
23
+ [![Python](https://img.shields.io/badge/Python-3.9%2B-blue.svg)](https://pypi.org/project/aegis-kernel/)
24
+ [![Tests](https://img.shields.io/badge/tests-11%2F11%20passing-brightgreen.svg)](#)
25
+
26
+ ---
27
+
28
+ ## 🚀 Installation
29
+
30
+ ```bash
31
+ pip install aegis-kernel
32
+ ```
33
+
34
+ ---
35
+
36
+ ## ⚡ Quickstart
37
+
38
+ ### Protect Database Tools
39
+ ```python
40
+ from aegis_kernel import aegis_guard
41
+
42
+ @aegis_guard(tool_name="database_exec")
43
+ def execute_sql(query: str):
44
+ # Automatically blocks destructive SQL (DELETE without WHERE, DROP TABLE, etc.)
45
+ return db.execute(query)
46
+ ```
47
+
48
+ ### Protect Financial & Payout Operations
49
+ ```python
50
+ from aegis_kernel import aegis_guard
51
+
52
+ @aegis_guard(tool_name="payout_tool")
53
+ def transfer_funds(amount: float, recipient_id: str):
54
+ # Automatically blocks transactions exceeding numeric risk limits
55
+ return payment_gateway.transfer(amount, recipient_id)
56
+ ```
57
+
58
+ ### CrewAI & AutoGen Integration
59
+ ```python
60
+ from aegis_kernel import AegisCrewAITool
61
+
62
+ safe_tool = AegisCrewAITool(my_existing_tool)
63
+ ```
64
+
65
+ ---
66
+
67
+ ## 📄 License
68
+
69
+ Distributed under the [MIT License](https://opensource.org/licenses/MIT). Copyright (c) 2026 Sneh Gabani.
@@ -0,0 +1,53 @@
1
+ # Aegis Invariant Kernel for Python
2
+
3
+ > **Deterministic Tool-Call Safety Gateway for Autonomous AI Agents**
4
+ > *Sub-0.1ms Latency • Zero External Dependencies • Zero Network Egress*
5
+
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
7
+ [![Python](https://img.shields.io/badge/Python-3.9%2B-blue.svg)](https://pypi.org/project/aegis-kernel/)
8
+ [![Tests](https://img.shields.io/badge/tests-11%2F11%20passing-brightgreen.svg)](#)
9
+
10
+ ---
11
+
12
+ ## 🚀 Installation
13
+
14
+ ```bash
15
+ pip install aegis-kernel
16
+ ```
17
+
18
+ ---
19
+
20
+ ## ⚡ Quickstart
21
+
22
+ ### Protect Database Tools
23
+ ```python
24
+ from aegis_kernel import aegis_guard
25
+
26
+ @aegis_guard(tool_name="database_exec")
27
+ def execute_sql(query: str):
28
+ # Automatically blocks destructive SQL (DELETE without WHERE, DROP TABLE, etc.)
29
+ return db.execute(query)
30
+ ```
31
+
32
+ ### Protect Financial & Payout Operations
33
+ ```python
34
+ from aegis_kernel import aegis_guard
35
+
36
+ @aegis_guard(tool_name="payout_tool")
37
+ def transfer_funds(amount: float, recipient_id: str):
38
+ # Automatically blocks transactions exceeding numeric risk limits
39
+ return payment_gateway.transfer(amount, recipient_id)
40
+ ```
41
+
42
+ ### CrewAI & AutoGen Integration
43
+ ```python
44
+ from aegis_kernel import AegisCrewAITool
45
+
46
+ safe_tool = AegisCrewAITool(my_existing_tool)
47
+ ```
48
+
49
+ ---
50
+
51
+ ## 📄 License
52
+
53
+ Distributed under the [MIT License](https://opensource.org/licenses/MIT). Copyright (c) 2026 Sneh Gabani.
@@ -0,0 +1,30 @@
1
+ """
2
+ Aegis Invariant Kernel for Python
3
+ Deterministic, sub-2ms safety clearance gateway for AI Agent tool execution.
4
+ """
5
+
6
+ from .types import AegisVerdict, AegisViolation, ToolCall, AegisConfig
7
+ from .engine import AegisEngine
8
+ from .decorator import aegis_guard, AegisBlockedError
9
+ from .checkers import PythonStateChecker, PythonPiiTokenVault
10
+ from .adapters import AegisCrewAITool, wrap_autogen_function, AegisLangChainTool, wrap_langchain_tool
11
+
12
+ __all__ = [
13
+ "AegisVerdict",
14
+ "AegisViolation",
15
+ "ToolCall",
16
+ "AegisConfig",
17
+ "AegisEngine",
18
+ "aegis_guard",
19
+ "AegisBlockedError",
20
+ "AegisCrewAITool",
21
+ "wrap_autogen_function",
22
+ "AegisLangChainTool",
23
+ "wrap_langchain_tool",
24
+ "PythonStateChecker",
25
+ "PythonPiiTokenVault",
26
+ ]
27
+
28
+ __version__ = "1.0.0"
29
+
30
+
@@ -0,0 +1,163 @@
1
+ """
2
+ Framework adapters for popular Python AI Agent Frameworks (CrewAI, AutoGen, LangChain).
3
+ """
4
+
5
+ import asyncio
6
+ from typing import Any, Callable, Dict, Optional, List, Union
7
+ from .engine import AegisEngine
8
+ from .decorator import aegis_guard, AegisBlockedError
9
+ from .types import ToolCall, AegisVerdict
10
+
11
+ class AegisCrewAITool:
12
+ """
13
+ CrewAI Tool Wrapper: Wraps a CrewAI BaseTool or tool function
14
+ with deterministic Aegis invariant verification.
15
+ """
16
+ def __init__(self, tool_instance: Any, engine: Optional[AegisEngine] = None):
17
+ self.tool = tool_instance
18
+ self.engine = engine or AegisEngine()
19
+ self.name = getattr(tool_instance, "name", getattr(tool_instance, "__name__", "crewai_tool"))
20
+ self.description = getattr(tool_instance, "description", "")
21
+
22
+ def run(self, *args: Any, **kwargs: Any) -> Any:
23
+ params = dict(kwargs)
24
+ if args:
25
+ params["_args"] = list(args)
26
+
27
+ tool_call = ToolCall(tool=self.name, params=params)
28
+ verdict = self.engine.evaluate(tool_call)
29
+
30
+ if not verdict.allowed:
31
+ first_v = verdict.violations[0]
32
+ # Return structured error string for CrewAI self-healing loop
33
+ return f"ERROR [Aegis Policy Blocked]: {first_v.rule_id} - {first_v.message}. Suggested Fix: {first_v.suggested_fix or 'Adhere to policy bounds.'}"
34
+
35
+ if hasattr(self.tool, "_run"):
36
+ return self.tool._run(*args, **kwargs)
37
+ elif callable(self.tool):
38
+ return self.tool(*args, **kwargs)
39
+ else:
40
+ raise ValueError(f"Target {self.tool} is not callable")
41
+
42
+ def __call__(self, *args: Any, **kwargs: Any) -> Any:
43
+ return self.run(*args, **kwargs)
44
+
45
+ def wrap_autogen_function(func: Callable[..., Any], engine: Optional[AegisEngine] = None) -> Callable[..., Any]:
46
+ """
47
+ Wraps an AutoGen tool function to intercept executions and return
48
+ self-healing structured feedback if an invariant is violated.
49
+ """
50
+ eng = engine or AegisEngine()
51
+ tool_name = func.__name__
52
+
53
+ def wrapped(*args: Any, **kwargs: Any) -> Any:
54
+ params = dict(kwargs)
55
+ if args:
56
+ params["_args"] = list(args)
57
+
58
+ tool_call = ToolCall(tool=tool_name, params=params)
59
+ verdict = eng.evaluate(tool_call)
60
+
61
+ if not verdict.allowed:
62
+ first_v = verdict.violations[0]
63
+ return {
64
+ "error": True,
65
+ "status": "BLOCKED",
66
+ "rule_id": first_v.rule_id,
67
+ "message": first_v.message,
68
+ "suggested_fix": first_v.suggested_fix,
69
+ "proof_hash": verdict.proof_hash
70
+ }
71
+
72
+ return func(*args, **kwargs)
73
+
74
+ return wrapped
75
+
76
+ class AegisLangChainTool:
77
+ """
78
+ LangChain Tool Wrapper: Wraps a LangChain BaseTool, StructuredTool, or Python callable
79
+ with deterministic Aegis invariant verification with zero external dependencies.
80
+ Supports both traditional `.run()` and modern LCEL / LangGraph `.invoke()` / `.ainvoke()`.
81
+ """
82
+ def __init__(
83
+ self,
84
+ tool_instance: Any,
85
+ engine: Optional[AegisEngine] = None,
86
+ handle_tool_error: bool = True
87
+ ):
88
+ self.tool = tool_instance
89
+ self.engine = engine or AegisEngine()
90
+ self.name = getattr(tool_instance, "name", getattr(tool_instance, "__name__", "langchain_tool"))
91
+ self.description = getattr(tool_instance, "description", "")
92
+ self.handle_tool_error = handle_tool_error
93
+
94
+ def _extract_params(self, tool_input: Any = None, *args: Any, **kwargs: Any) -> Dict[str, Any]:
95
+ params = dict(kwargs)
96
+ if tool_input is not None:
97
+ if isinstance(tool_input, dict):
98
+ params.update(tool_input)
99
+ elif isinstance(tool_input, str):
100
+ if "query" not in params and "input" not in params:
101
+ params["query"] = tool_input
102
+ else:
103
+ params["_input"] = tool_input
104
+ else:
105
+ params["_tool_input"] = tool_input
106
+ if args:
107
+ params["_args"] = list(args)
108
+ return params
109
+
110
+ def run(self, tool_input: Any = None, *args: Any, **kwargs: Any) -> Any:
111
+ params = self._extract_params(tool_input, *args, **kwargs)
112
+ tool_call = ToolCall(tool=self.name, params=params)
113
+ verdict = self.engine.evaluate(tool_call)
114
+
115
+ if not verdict.allowed:
116
+ first_v = verdict.violations[0]
117
+ if self.handle_tool_error:
118
+ return f"Error: [Aegis Policy Blocked] {first_v.rule_id} - {first_v.message}. Suggested Fix: {first_v.suggested_fix or 'Adhere to policy bounds.'}"
119
+ raise AegisBlockedError(verdict)
120
+
121
+ if hasattr(self.tool, "run") and callable(self.tool.run) and self.tool.run != self.run:
122
+ return self.tool.run(tool_input, *args, **kwargs) if tool_input is not None else self.tool.run(*args, **kwargs)
123
+ elif hasattr(self.tool, "_run") and callable(self.tool._run):
124
+ return self.tool._run(tool_input, *args, **kwargs) if tool_input is not None else self.tool._run(*args, **kwargs)
125
+ elif callable(self.tool):
126
+ if tool_input is not None and not kwargs and not args:
127
+ return self.tool(tool_input)
128
+ return self.tool(*args, **kwargs)
129
+ else:
130
+ raise ValueError(f"Target {self.tool} is not callable")
131
+
132
+ def invoke(self, input: Any, config: Optional[Dict[str, Any]] = None, **kwargs: Any) -> Any:
133
+ """LangChain Runnable Protocol compatibility."""
134
+ return self.run(input, **kwargs)
135
+
136
+ async def ainvoke(self, input: Any, config: Optional[Dict[str, Any]] = None, **kwargs: Any) -> Any:
137
+ """Async LangChain Runnable Protocol compatibility."""
138
+ params = self._extract_params(input, **kwargs)
139
+ tool_call = ToolCall(tool=self.name, params=params)
140
+ verdict = self.engine.evaluate(tool_call)
141
+
142
+ if not verdict.allowed:
143
+ first_v = verdict.violations[0]
144
+ if self.handle_tool_error:
145
+ return f"Error: [Aegis Policy Blocked] {first_v.rule_id} - {first_v.message}. Suggested Fix: {first_v.suggested_fix or 'Adhere to policy bounds.'}"
146
+ raise AegisBlockedError(verdict)
147
+
148
+ if hasattr(self.tool, "ainvoke") and callable(self.tool.ainvoke):
149
+ return await self.tool.ainvoke(input, config=config, **kwargs)
150
+ elif hasattr(self.tool, "_arun") and callable(self.tool._arun):
151
+ return await self.tool._arun(input, **kwargs)
152
+ elif asyncio.iscoroutinefunction(self.tool):
153
+ return await self.tool(input, **kwargs)
154
+ else:
155
+ return self.run(input, **kwargs)
156
+
157
+ def __call__(self, *args: Any, **kwargs: Any) -> Any:
158
+ return self.run(*args, **kwargs)
159
+
160
+ def wrap_langchain_tool(tool: Any, engine: Optional[AegisEngine] = None, handle_tool_error: bool = True) -> AegisLangChainTool:
161
+ """Convenience helper to wrap any LangChain tool or callable with Aegis invariant verification."""
162
+ return AegisLangChainTool(tool, engine=engine, handle_tool_error=handle_tool_error)
163
+
@@ -0,0 +1,275 @@
1
+ import re
2
+ from typing import Any, Dict, List, Optional
3
+ from .types import AegisViolation, ToolCall
4
+
5
+ PATTERNS = {
6
+ "CREDIT_CARD": re.compile(r"\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13})\b"),
7
+ "US_SSN": re.compile(r"\b\d{3}-\d{2}-\d{4}\b"),
8
+ "OPENAI_API_KEY": re.compile(r"\b(?:sk-ant-api[0-9a-zA-Z_-]{15,}|sk-(?:proj-|live-)?[a-zA-Z0-9_-]{20,})\b"),
9
+ "GITHUB_TOKEN": re.compile(r"\b(?:ghp|gho|ghu|ghs|ghr)_[a-zA-Z0-9]{20,}\b"),
10
+ "AWS_ACCESS_KEY": re.compile(r"\bAKIA[0-9A-Z]{16}\b"),
11
+ "STRIPE_KEY": re.compile(r"\b(?:sk|pk|rk)_(?:live|test)_[0-9a-zA-Z]{20,}\b"),
12
+ "CREDIT_CARD_CVV": re.compile(r"\b(?:cvv|cvc|cvn|cid)\s*[:=]\s*\d{3,4}\b", re.IGNORECASE),
13
+ "SENSITIVE_FILE_PATH": re.compile(r"(?:/etc/(?:shadow|passwd|sudoers)|\.ssh/(?:id_rsa|authorized_keys)|\.env(?:\.[a-zA-Z0-9_-]+)?|/proc/self/environ)"),
14
+ "US_NPI": re.compile(r"\b[12]\d{9}\b"),
15
+ "US_DEA": re.compile(r"\b[A-Z]{2}\d{7}\b"),
16
+ }
17
+
18
+ def looks_like_sql(val: str) -> bool:
19
+ if not isinstance(val, str):
20
+ return False
21
+ s = val.lstrip()
22
+ while s:
23
+ if s.startswith("--"):
24
+ idx = s.find("\n")
25
+ s = "" if idx == -1 else s[idx + 1:].lstrip()
26
+ elif s.startswith("/*"):
27
+ idx = s.find("*/")
28
+ s = "" if idx == -1 else s[idx + 2:].lstrip()
29
+ else:
30
+ break
31
+ return bool(re.match(r"^(?:SELECT|INSERT|UPDATE|DELETE|DROP|TRUNCATE|ALTER|CREATE|EXEC|EXECUTE|WITH)\b", s, re.IGNORECASE))
32
+
33
+
34
+ class PythonSqlChecker:
35
+ @staticmethod
36
+ def evaluate(rule_id: str, pack_id: str, params: Dict[str, Any], tool_call: ToolCall) -> List[AegisViolation]:
37
+ violations: List[AegisViolation] = []
38
+
39
+ strings: List[str] = []
40
+ def collect(obj: Any):
41
+ if isinstance(obj, str):
42
+ strings.append(obj)
43
+ elif isinstance(obj, list):
44
+ for item in obj: collect(item)
45
+ elif isinstance(obj, dict):
46
+ for val in obj.values(): collect(val)
47
+ collect(tool_call.params)
48
+
49
+ sql_text = ""
50
+ for val in strings:
51
+ if looks_like_sql(val):
52
+ sql_text = val
53
+ break
54
+
55
+ if not sql_text and strings:
56
+ for val in strings:
57
+ if any(kw in val.upper() for kw in ["DELETE", "DROP", "UPDATE", "TRUNCATE", "ALTER"]):
58
+ sql_text = val
59
+ break
60
+
61
+ if not sql_text:
62
+ return violations
63
+
64
+ # Strip string literals before searching for DDL statements (prevents false positives on note='DROP')
65
+ sql_without_strings = re.sub(r"'[^']*'", "''", sql_text)
66
+ upper_sql = sql_without_strings.upper()
67
+
68
+ # 1. Blocked statements (DROP, TRUNCATE, ALTER)
69
+ blocked_statements = params.get("block_statements", [])
70
+ for stmt in blocked_statements:
71
+ if re.search(rf"\b{stmt}\b", upper_sql):
72
+ violations.append(AegisViolation(
73
+ rule_id=rule_id,
74
+ pack_id=pack_id,
75
+ severity="critical",
76
+ message=f"Destructive SQL statement '{stmt}' is prohibited in production agent workflows.",
77
+ suggested_fix=f"Remove prohibited {stmt} statement from query.",
78
+ ))
79
+ return violations
80
+
81
+ # 2. Required WHERE clause for DELETE/UPDATE
82
+ statements = params.get("statements", [])
83
+ require = params.get("require")
84
+ for stmt in statements:
85
+ if re.search(rf"\b{stmt}\b", upper_sql):
86
+ if require == "WHERE_CLAUSE":
87
+ where_match = re.search(r"\bWHERE\b\s+(.*)", sql_without_strings, re.IGNORECASE | re.DOTALL)
88
+ if not where_match:
89
+ violations.append(AegisViolation(
90
+ rule_id=rule_id,
91
+ pack_id=pack_id,
92
+ severity="critical",
93
+ message=f"SQL {stmt} statement must include a valid targeted WHERE clause.",
94
+ suggested_fix=f"Add a WHERE condition to target specific rows.",
95
+ ))
96
+ else:
97
+ where_clause = where_match.group(1).strip()
98
+ # Comprehensive tautology check (1=1, 2>1, true, 1, id>0, id<>-1, IS NOT NULL)
99
+ is_tautology = bool(
100
+ re.search(r"^\s*(?:1\s*=\s*1|2\s*>\s*1|(\d+)\s*=\s*\1|(\d+)\s*>\s*(\d+)|true|1|'a'\s*=\s*'a')\s*(?:;)?$", where_clause, re.IGNORECASE)
101
+ or re.search(r"\bIS\s+NOT\s+NULL\b", where_clause, re.IGNORECASE)
102
+ or re.search(r"\b[a-zA-Z_]\w*\s*>\s*0\b", where_clause, re.IGNORECASE)
103
+ or re.search(r"\b[a-zA-Z_]\w*\s*(?:<>|!=)\s*-\d+\b", where_clause, re.IGNORECASE)
104
+ or re.search(r"\bIN\s*\(\s*SELECT\b", where_clause, re.IGNORECASE)
105
+ or re.search(r"\bOR\s+(?:1\s*=\s*1|true|1|\d+\s*>\s*\d+|'[^']+'\s*=\s*'[^']+')", where_clause, re.IGNORECASE)
106
+ )
107
+ if is_tautology:
108
+ violations.append(AegisViolation(
109
+ rule_id=rule_id,
110
+ pack_id=pack_id,
111
+ severity="critical",
112
+ message=f"SQL {stmt} contains constant tautology WHERE clause bypassing safety filters.",
113
+ suggested_fix="Replace tautology with authentic column filters.",
114
+ ))
115
+
116
+ return violations
117
+
118
+ class PythonPiiChecker:
119
+ @staticmethod
120
+ def evaluate(rule_id: str, pack_id: str, params: Dict[str, Any], tool_call: ToolCall) -> List[AegisViolation]:
121
+ violations: List[AegisViolation] = []
122
+ target_patterns = params.get("patterns", [])
123
+
124
+ # Collect all string values recursively
125
+ strings: List[str] = []
126
+ def collect(obj: Any):
127
+ if isinstance(obj, str):
128
+ strings.append(obj)
129
+ elif isinstance(obj, list):
130
+ for item in obj: collect(item)
131
+ elif isinstance(obj, dict):
132
+ for val in obj.values(): collect(val)
133
+ collect(tool_call.params)
134
+
135
+ for pat_name in target_patterns:
136
+ regex = PATTERNS.get(pat_name) or re.compile(pat_name, re.IGNORECASE)
137
+ for text in strings:
138
+ if regex.search(text):
139
+ violations.append(AegisViolation(
140
+ rule_id=rule_id,
141
+ pack_id=pack_id,
142
+ severity="critical",
143
+ message=f"Sensitive pattern '{pat_name}' detected in tool arguments.",
144
+ suggested_fix=f"Redact or parameterize sensitive tokens before invoking tool '{tool_call.tool}'.",
145
+ ))
146
+ break
147
+
148
+ return violations
149
+
150
+ class PythonNumericChecker:
151
+ @staticmethod
152
+ def evaluate(rule_id: str, pack_id: str, params: Dict[str, Any], tool_call: ToolCall) -> List[AegisViolation]:
153
+ violations: List[AegisViolation] = []
154
+ field_name = params.get("field", "amount")
155
+ max_val = params.get("max")
156
+ min_val = params.get("min", 0 if any(k in field_name.lower() for k in ["amount", "price", "cost", "payment", "payout", "transfer"]) else None)
157
+
158
+ def parse_number(val: Any) -> Optional[float]:
159
+ if isinstance(val, (int, float)):
160
+ return float(val)
161
+ if isinstance(val, str):
162
+ cleaned = re.sub(r"[$€£¥₹,]", "", val).strip()
163
+ try:
164
+ return float(cleaned)
165
+ except ValueError:
166
+ return None
167
+ return None
168
+
169
+ val = None
170
+ # Check direct field
171
+ if field_name in tool_call.params:
172
+ val = parse_number(tool_call.params[field_name])
173
+
174
+ # Check semantic aliases (total, value, sum, price, payout, cost)
175
+ if val is None:
176
+ aliases = ["amount", "total", "value", "sum", "price", "cost", "payout", "payment", "transfer"]
177
+ for alias in aliases:
178
+ for k, v in tool_call.params.items():
179
+ if k.lower() == alias:
180
+ parsed = parse_number(v)
181
+ if parsed is not None:
182
+ val = parsed
183
+ break
184
+ if val is not None:
185
+ break
186
+
187
+ if val is None and "_args" in tool_call.params and isinstance(tool_call.params["_args"], list):
188
+ for arg in tool_call.params["_args"]:
189
+ parsed = parse_number(arg)
190
+ if parsed is not None:
191
+ val = parsed
192
+ break
193
+
194
+ if val is not None:
195
+ if max_val is not None and val > max_val:
196
+ violations.append(AegisViolation(
197
+ rule_id=rule_id,
198
+ pack_id=pack_id,
199
+ severity="critical",
200
+ message=f"Field '{field_name}' with value {val} exceeds maximum allowed boundary of {max_val}.",
201
+ suggested_fix=f"Constrain {field_name} <= {max_val}.",
202
+ ))
203
+ if min_val is not None and val < min_val:
204
+ violations.append(AegisViolation(
205
+ rule_id=rule_id,
206
+ pack_id=pack_id,
207
+ severity="critical",
208
+ message=f"Field '{field_name}' with value {val} is below minimum allowed boundary of {min_val}.",
209
+ suggested_fix=f"Ensure {field_name} >= {min_val}.",
210
+ ))
211
+
212
+ return violations
213
+
214
+ class PythonStateChecker:
215
+ @staticmethod
216
+ def evaluate(rule_id: str, pack_id: str, params: Dict[str, Any], tool_call: ToolCall, state: Optional[Dict[str, Any]] = None) -> List[AegisViolation]:
217
+ violations: List[AegisViolation] = []
218
+ current_state = state or {}
219
+
220
+ # 1. Multi-tenant isolation check
221
+ tenant_field = params.get("tenant_field")
222
+ if tenant_field:
223
+ expected_tenant = current_state.get(tenant_field)
224
+ call_tenant = tool_call.params.get(tenant_field)
225
+ if expected_tenant is not None and call_tenant is not None and str(expected_tenant) != str(call_tenant):
226
+ violations.append(AegisViolation(
227
+ rule_id=rule_id,
228
+ pack_id=pack_id,
229
+ severity="critical",
230
+ message=f"Cross-tenant parameter mismatch: request tenant '{call_tenant}' != session tenant '{expected_tenant}'.",
231
+ suggested_fix="Align request tenant with authenticated session context.",
232
+ ))
233
+
234
+ # 2. Target field state assertion (e.g. order_status != 'cancelled')
235
+ target_field = params.get("target_field")
236
+ assertion = params.get("assertion")
237
+ if target_field and assertion:
238
+ target_val = tool_call.params.get(target_field)
239
+ if target_val:
240
+ order_status = current_state.get("order_status")
241
+ if order_status == "cancelled" and "cancelled" in assertion:
242
+ violations.append(AegisViolation(
243
+ rule_id=rule_id,
244
+ pack_id=pack_id,
245
+ severity="critical",
246
+ message=f"Operation on '{target_field}={target_val}' prohibited when state is '{order_status}'.",
247
+ suggested_fix="Check entity status before requesting mutation.",
248
+ ))
249
+
250
+ return violations
251
+
252
+ class PythonPiiTokenVault:
253
+ def __init__(self, salt: Optional[str] = None):
254
+ import secrets
255
+ self.salt = salt or secrets.token_hex(16)
256
+ self.vault: Dict[str, str] = {}
257
+ self.reverse_vault: Dict[str, str] = {}
258
+
259
+ def tokenize(self, value: str, token_type: str = "PII") -> str:
260
+ if value in self.vault:
261
+ return self.vault[value]
262
+
263
+ import hashlib
264
+ digest = hashlib.sha256(f"{self.salt}:{value}".encode()).hexdigest()[:16]
265
+ token = f"<AEGIS_{token_type}_{digest}>"
266
+ self.vault[value] = token
267
+ self.reverse_vault[token] = value
268
+ return token
269
+
270
+ def detokenize(self, text: str) -> str:
271
+ result = text
272
+ for token, original in self.reverse_vault.items():
273
+ result = result.replace(token, original)
274
+ return result
275
+
@@ -0,0 +1,63 @@
1
+ import asyncio
2
+ import functools
3
+ from typing import Any, Callable, Optional, List, Dict
4
+ from .engine import AegisEngine
5
+ from .types import ToolCall, AegisVerdict
6
+
7
+ class AegisBlockedError(Exception):
8
+ def __init__(self, verdict: AegisVerdict):
9
+ self.verdict = verdict
10
+ rule_info = verdict.violations[0].rule_id if verdict.violations else "INVARIANT-POLICY"
11
+ msg_info = verdict.violations[0].message if verdict.violations else "Security policy constraint violated"
12
+ msg = f"Aegis Invariant Violation: Tool execution blocked. Rule: {rule_info} - {msg_info}"
13
+ super().__init__(msg)
14
+
15
+ def aegis_guard(
16
+ tool_name: Optional[str] = None,
17
+ mode: str = "enforce",
18
+ rules: Optional[List[Dict[str, Any]]] = None,
19
+ engine: Optional[AegisEngine] = None
20
+ ):
21
+ """
22
+ Decorator to wrap Python agent tool functions (sync or async coroutines)
23
+ with sub-2ms deterministic Aegis invariant verification.
24
+ """
25
+ active_engine = engine if engine is not None else AegisEngine(mode=mode, rules=rules)
26
+
27
+ def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
28
+ actual_name = tool_name or func.__name__
29
+
30
+ if asyncio.iscoroutinefunction(func):
31
+ @functools.wraps(func)
32
+ async def async_wrapper(*args: Any, **kwargs: Any) -> Any:
33
+ params = dict(kwargs)
34
+ if args:
35
+ params["_args"] = list(args)
36
+
37
+ tool_call = ToolCall(tool=actual_name, params=params)
38
+ verdict = active_engine.evaluate(tool_call)
39
+
40
+ if not verdict.allowed:
41
+ raise AegisBlockedError(verdict)
42
+
43
+ return await func(*args, **kwargs)
44
+
45
+ return async_wrapper
46
+ else:
47
+ @functools.wraps(func)
48
+ def sync_wrapper(*args: Any, **kwargs: Any) -> Any:
49
+ params = dict(kwargs)
50
+ if args:
51
+ params["_args"] = list(args)
52
+
53
+ tool_call = ToolCall(tool=actual_name, params=params)
54
+ verdict = active_engine.evaluate(tool_call)
55
+
56
+ if not verdict.allowed:
57
+ raise AegisBlockedError(verdict)
58
+
59
+ return func(*args, **kwargs)
60
+
61
+ return sync_wrapper
62
+
63
+ return decorator
@@ -0,0 +1,117 @@
1
+ import time
2
+ import hashlib
3
+ import json
4
+ from typing import Any, Dict, List, Optional, Union
5
+ from .types import AegisVerdict, AegisViolation, ToolCall, AegisConfig
6
+ from .checkers import PythonSqlChecker, PythonPiiChecker, PythonNumericChecker, PythonStateChecker, PythonPiiTokenVault
7
+
8
+ BUILTIN_RULES = [
9
+ # SQL Guard
10
+ {
11
+ "id": "SQL-001",
12
+ "pack_id": "sql-guard",
13
+ "type": "sql",
14
+ "params": {"statements": ["DELETE", "UPDATE"], "require": "WHERE_CLAUSE"}
15
+ },
16
+ {
17
+ "id": "SQL-002",
18
+ "pack_id": "sql-guard",
19
+ "type": "sql",
20
+ "params": {"block_statements": ["DROP", "TRUNCATE", "ALTER"]}
21
+ },
22
+ # Finance Guard
23
+ {
24
+ "id": "FIN-001",
25
+ "pack_id": "finance-guard",
26
+ "type": "numeric",
27
+ "params": {"field": "amount", "max": 10000}
28
+ },
29
+ # Data Guard
30
+ {
31
+ "id": "DATA-001",
32
+ "pack_id": "data-guard",
33
+ "type": "pii",
34
+ "params": {"patterns": ["CREDIT_CARD", "US_SSN", "CREDIT_CARD_CVV"]}
35
+ },
36
+ {
37
+ "id": "DATA-002",
38
+ "pack_id": "data-guard",
39
+ "type": "pii",
40
+ "params": {"patterns": ["OPENAI_API_KEY", "GITHUB_TOKEN", "AWS_ACCESS_KEY", "STRIPE_KEY"]}
41
+ },
42
+ # SOC 2 & HIPAA
43
+ {
44
+ "id": "SOC2-001",
45
+ "pack_id": "soc2-guard",
46
+ "type": "pii",
47
+ "params": {"patterns": ["SENSITIVE_FILE_PATH"]}
48
+ },
49
+ {
50
+ "id": "HIPAA-001",
51
+ "pack_id": "hipaa-guard",
52
+ "type": "pii",
53
+ "params": {"patterns": ["US_NPI", "US_DEA"]}
54
+ }
55
+ ]
56
+
57
+ class AegisEngine:
58
+ def __init__(
59
+ self,
60
+ config: Optional[Union[AegisConfig, str]] = None,
61
+ rules: Optional[List[Dict[str, Any]]] = None,
62
+ mode: str = "enforce",
63
+ fail_policy: str = "fail-closed",
64
+ packs: Optional[List[str]] = None,
65
+ ):
66
+ if isinstance(config, AegisConfig):
67
+ self.mode = config.mode
68
+ self.fail_policy = config.fail_policy
69
+ self.rules = config.rules or rules or BUILTIN_RULES
70
+ elif isinstance(config, str):
71
+ self.mode = config
72
+ self.fail_policy = fail_policy
73
+ self.rules = rules or BUILTIN_RULES
74
+ else:
75
+ self.mode = mode
76
+ self.fail_policy = fail_policy
77
+ self.rules = rules or BUILTIN_RULES
78
+ self.policy_hash = hashlib.sha256(json.dumps(self.rules, sort_keys=True).encode()).hexdigest()
79
+
80
+ def evaluate(self, tool_call: ToolCall, state: Optional[Dict[str, Any]] = None) -> AegisVerdict:
81
+ start_time = time.perf_counter()
82
+ violations: List[AegisViolation] = []
83
+
84
+ for rule in self.rules:
85
+ rule_type = rule.get("type")
86
+ rule_id = rule.get("id", "UNKNOWN")
87
+ pack_id = rule.get("pack_id", "custom")
88
+ params = rule.get("params", {})
89
+
90
+ if rule_type == "sql" or rule_type == "sql_ast":
91
+ violations.extend(PythonSqlChecker.evaluate(rule_id, pack_id, params, tool_call))
92
+ elif rule_type == "pii" or rule_type == "regex":
93
+ violations.extend(PythonPiiChecker.evaluate(rule_id, pack_id, params, tool_call))
94
+ elif rule_type == "numeric":
95
+ violations.extend(PythonNumericChecker.evaluate(rule_id, pack_id, params, tool_call))
96
+ elif rule_type == "state" or rule_type == "state_invariant":
97
+ violations.extend(PythonStateChecker.evaluate(rule_id, pack_id, params, tool_call, state))
98
+
99
+ latency_ms = (time.perf_counter() - start_time) * 1000.0
100
+
101
+ # Cryptographic proof hash
102
+ proof_payload = f"{tool_call.tool}:{json.dumps(tool_call.params, sort_keys=True)}:{self.policy_hash}"
103
+ proof_hash = hashlib.sha256(proof_payload.encode()).hexdigest()
104
+
105
+ is_allowed = len(violations) == 0 or self.mode == "shadow"
106
+ verdict_str = "ALLOWED" if is_allowed else "BLOCKED"
107
+ suggested_fix = violations[0].suggested_fix if violations else None
108
+
109
+ return AegisVerdict(
110
+ allowed=is_allowed,
111
+ verdict=verdict_str,
112
+ violations=violations,
113
+ latency_ms=round(latency_ms, 3),
114
+ proof_hash=proof_hash,
115
+ policy_commitment_hash=self.policy_hash,
116
+ suggested_fix=suggested_fix,
117
+ )
@@ -0,0 +1,36 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import Any, Dict, List, Optional, Literal
3
+
4
+ @dataclass
5
+ class AegisViolation:
6
+ rule_id: str
7
+ pack_id: str
8
+ severity: Literal["critical", "warning", "info"]
9
+ message: str
10
+ suggested_fix: Optional[str] = None
11
+ context: Dict[str, Any] = field(default_factory=dict)
12
+
13
+ @dataclass
14
+ class AegisVerdict:
15
+ allowed: bool
16
+ verdict: Literal["ALLOWED", "BLOCKED"]
17
+ violations: List[AegisViolation] = field(default_factory=list)
18
+ latency_ms: float = 0.0
19
+ proof_hash: str = ""
20
+ policy_commitment_hash: str = ""
21
+ suggested_fix: Optional[str] = None
22
+
23
+ @property
24
+ def valid(self) -> bool:
25
+ return self.allowed
26
+
27
+ @dataclass
28
+ class AegisConfig:
29
+ mode: Literal["enforce", "monitor", "simulate"] = "enforce"
30
+ fail_policy: Literal["fail-closed", "fail-open"] = "fail-closed"
31
+ rules: Optional[List[Dict[str, Any]]] = None
32
+
33
+ @dataclass
34
+ class ToolCall:
35
+ tool: str
36
+ params: Dict[str, Any]
@@ -0,0 +1,69 @@
1
+ Metadata-Version: 2.4
2
+ Name: aegis-kernel
3
+ Version: 1.0.0
4
+ Summary: Aegis Invariant Kernel: Deterministic Tool-Call Safety Clearance Gateway for AI Agents
5
+ Author-email: Sneh Gabani <sneh@aegis-kernel.dev>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://aegis-kernel.dev
8
+ Project-URL: Repository, https://github.com/Snehgabani/aegis-kernel
9
+ Keywords: ai-safety,agentic-ai,langchain,crewai,autogen,mcp,security,invariants
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.9
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Dynamic: license-file
16
+
17
+ # Aegis Invariant Kernel for Python
18
+
19
+ > **Deterministic Tool-Call Safety Gateway for Autonomous AI Agents**
20
+ > *Sub-0.1ms Latency • Zero External Dependencies • Zero Network Egress*
21
+
22
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
23
+ [![Python](https://img.shields.io/badge/Python-3.9%2B-blue.svg)](https://pypi.org/project/aegis-kernel/)
24
+ [![Tests](https://img.shields.io/badge/tests-11%2F11%20passing-brightgreen.svg)](#)
25
+
26
+ ---
27
+
28
+ ## 🚀 Installation
29
+
30
+ ```bash
31
+ pip install aegis-kernel
32
+ ```
33
+
34
+ ---
35
+
36
+ ## ⚡ Quickstart
37
+
38
+ ### Protect Database Tools
39
+ ```python
40
+ from aegis_kernel import aegis_guard
41
+
42
+ @aegis_guard(tool_name="database_exec")
43
+ def execute_sql(query: str):
44
+ # Automatically blocks destructive SQL (DELETE without WHERE, DROP TABLE, etc.)
45
+ return db.execute(query)
46
+ ```
47
+
48
+ ### Protect Financial & Payout Operations
49
+ ```python
50
+ from aegis_kernel import aegis_guard
51
+
52
+ @aegis_guard(tool_name="payout_tool")
53
+ def transfer_funds(amount: float, recipient_id: str):
54
+ # Automatically blocks transactions exceeding numeric risk limits
55
+ return payment_gateway.transfer(amount, recipient_id)
56
+ ```
57
+
58
+ ### CrewAI & AutoGen Integration
59
+ ```python
60
+ from aegis_kernel import AegisCrewAITool
61
+
62
+ safe_tool = AegisCrewAITool(my_existing_tool)
63
+ ```
64
+
65
+ ---
66
+
67
+ ## 📄 License
68
+
69
+ Distributed under the [MIT License](https://opensource.org/licenses/MIT). Copyright (c) 2026 Sneh Gabani.
@@ -0,0 +1,14 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ aegis_kernel/__init__.py
5
+ aegis_kernel/adapters.py
6
+ aegis_kernel/checkers.py
7
+ aegis_kernel/decorator.py
8
+ aegis_kernel/engine.py
9
+ aegis_kernel/types.py
10
+ aegis_kernel.egg-info/PKG-INFO
11
+ aegis_kernel.egg-info/SOURCES.txt
12
+ aegis_kernel.egg-info/dependency_links.txt
13
+ aegis_kernel.egg-info/top_level.txt
14
+ tests/test_aegis.py
@@ -0,0 +1 @@
1
+ aegis_kernel
@@ -0,0 +1,21 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "aegis-kernel"
7
+ version = "1.0.0"
8
+ description = "Aegis Invariant Kernel: Deterministic Tool-Call Safety Clearance Gateway for AI Agents"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "MIT"
12
+ authors = [{ name = "Sneh Gabani", email = "sneh@aegis-kernel.dev" }]
13
+ keywords = ["ai-safety", "agentic-ai", "langchain", "crewai", "autogen", "mcp", "security", "invariants"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "Operating System :: OS Independent",
17
+ ]
18
+
19
+ [project.urls]
20
+ Homepage = "https://aegis-kernel.dev"
21
+ Repository = "https://github.com/Snehgabani/aegis-kernel"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,288 @@
1
+ import unittest
2
+ import sys
3
+ import os
4
+
5
+ # Add package directory to path for testing
6
+ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
7
+
8
+ from aegis_kernel import (
9
+ AegisEngine,
10
+ ToolCall,
11
+ aegis_guard,
12
+ AegisBlockedError,
13
+ AegisCrewAITool,
14
+ wrap_autogen_function,
15
+ AegisLangChainTool,
16
+ wrap_langchain_tool,
17
+ PythonStateChecker,
18
+ PythonPiiTokenVault,
19
+ )
20
+
21
+ class TestAegisPythonKernel(unittest.TestCase):
22
+ def setUp(self):
23
+ self.engine = AegisEngine(mode="enforce")
24
+
25
+ def test_blocks_destructive_sql_operations(self):
26
+ # 1. Mass DELETE without WHERE
27
+ call1 = ToolCall(tool="db_exec", params={"query": "DELETE FROM users"})
28
+ v1 = self.engine.evaluate(call1)
29
+ self.assertFalse(v1.allowed)
30
+ self.assertEqual(v1.verdict, "BLOCKED")
31
+ self.assertEqual(v1.violations[0].rule_id, "SQL-001")
32
+
33
+ # 2. DROP TABLE
34
+ call2 = ToolCall(tool="db_exec", params={"query": "DROP TABLE accounts;"})
35
+ v2 = self.engine.evaluate(call2)
36
+ self.assertFalse(v2.allowed)
37
+ self.assertEqual(v2.violations[0].rule_id, "SQL-002")
38
+
39
+ # 3. Legitimate targeted SELECT with string literal containing DROP
40
+ call3 = ToolCall(tool="db_exec", params={"query": "SELECT * FROM t WHERE note = 'DROP'"})
41
+ v3 = self.engine.evaluate(call3)
42
+ self.assertTrue(v3.allowed)
43
+ self.assertEqual(v3.verdict, "ALLOWED")
44
+
45
+ # 4. Tautology bypass attempts (WHERE 2>1, WHERE 1, WHERE id>0)
46
+ for tautology in ["DELETE FROM users WHERE 2>1", "DELETE FROM users WHERE 1", "DELETE FROM users WHERE id > 0"]:
47
+ call_t = ToolCall(tool="db_exec", params={"query": tautology})
48
+ v_t = self.engine.evaluate(call_t)
49
+ self.assertFalse(v_t.allowed, f"Expected {tautology} to be BLOCKED")
50
+ self.assertEqual(v_t.violations[0].rule_id, "SQL-001")
51
+
52
+ # 5. Tool-name and param-name alias bypasses (tools/call with stmt)
53
+ call_alias = ToolCall(tool="tools/call", params={"stmt": "DROP TABLE users"})
54
+ v_alias = self.engine.evaluate(call_alias)
55
+ self.assertFalse(v_alias.allowed)
56
+ self.assertEqual(v_alias.violations[0].rule_id, "SQL-002")
57
+
58
+ # 6. Financial alias overspend (total/price/value instead of amount)
59
+ for alias_field in ["total", "price", "value", "sum", "payout"]:
60
+ call_f = ToolCall(tool="payment", params={alias_field: 50000})
61
+ v_f = self.engine.evaluate(call_f)
62
+ self.assertFalse(v_f.allowed, f"Expected financial limit on {alias_field} to be BLOCKED")
63
+ self.assertEqual(v_f.violations[0].rule_id, "FIN-001")
64
+
65
+ # 7. Negative amount & formatted currency string
66
+ call_neg = ToolCall(tool="transfer", params={"amount": -100})
67
+ self.assertFalse(self.engine.evaluate(call_neg).allowed)
68
+
69
+ call_str = ToolCall(tool="transfer", params={"amount": "$50,000.00"})
70
+ self.assertFalse(self.engine.evaluate(call_str).allowed)
71
+
72
+ def test_blocks_financial_limits_and_pii(self):
73
+ # Overspend
74
+ fin_call = ToolCall(tool="transfer_funds", params={"amount": 50000})
75
+ v_fin = self.engine.evaluate(fin_call)
76
+ self.assertFalse(v_fin.allowed)
77
+ self.assertEqual(v_fin.violations[0].rule_id, "FIN-001")
78
+
79
+ # OpenAI API key leak
80
+ key_call = ToolCall(tool="post_message", params={"body": "My token is sk-ant-api03-abcdef1234567890"})
81
+ v_key = self.engine.evaluate(key_call)
82
+ self.assertFalse(v_key.allowed)
83
+ self.assertEqual(v_key.violations[0].rule_id, "DATA-002")
84
+
85
+ # System file traversal
86
+ sec_call = ToolCall(tool="read_file", params={"path": "/etc/shadow"})
87
+ v_sec = self.engine.evaluate(sec_call)
88
+ self.assertFalse(v_sec.allowed)
89
+ self.assertEqual(v_sec.violations[0].rule_id, "SOC2-001")
90
+
91
+ def test_python_decorator_guard(self):
92
+ @aegis_guard(tool_name="database_runner")
93
+ def execute_db(query: str):
94
+ return f"Executed: {query}"
95
+
96
+ # Should raise AegisBlockedError on rogue call
97
+ with self.assertRaises(AegisBlockedError):
98
+ execute_db(query="DELETE FROM transactions")
99
+
100
+ # Should succeed on safe call
101
+ result = execute_db(query="SELECT * FROM transactions WHERE id = 100")
102
+ self.assertEqual(result, "Executed: SELECT * FROM transactions WHERE id = 100")
103
+
104
+ def test_crewai_adapter(self):
105
+ class MockCrewTool:
106
+ name = "execute_sql"
107
+ def _run(self, query: str):
108
+ return f"Success: {query}"
109
+
110
+ guarded = AegisCrewAITool(MockCrewTool(), engine=self.engine)
111
+
112
+ # Blocked call returns structured error string
113
+ err_res = guarded.run(query="DROP TABLE secret_records;")
114
+ self.assertTrue("ERROR [Aegis Policy Blocked]: SQL-002" in err_res)
115
+
116
+ # Safe call executes underlying _run
117
+ ok_res = guarded.run(query="SELECT * FROM users WHERE id = 5;")
118
+ self.assertEqual(ok_res, "Success: SELECT * FROM users WHERE id = 5;")
119
+
120
+ def test_autogen_adapter(self):
121
+ def transfer_funds(amount: float, recipient: str):
122
+ return f"Transferred ${amount} to {recipient}"
123
+
124
+ safe_transfer = wrap_autogen_function(transfer_funds, engine=self.engine)
125
+
126
+ # Blocked call returns error dict
127
+ blocked_dict = safe_transfer(amount=99999, recipient="attacker")
128
+ self.assertTrue(blocked_dict.get("error"))
129
+ self.assertEqual(blocked_dict.get("status"), "BLOCKED")
130
+ self.assertEqual(blocked_dict.get("rule_id"), "FIN-001")
131
+
132
+ # Allowed call executes
133
+ res = safe_transfer(amount=100, recipient="alice")
134
+ self.assertEqual(res, "Transferred $100 to alice")
135
+
136
+ def test_langchain_adapter(self):
137
+ class MockLangChainTool:
138
+ name = "sql_db_query"
139
+ description = "Executes SQL queries against the database"
140
+
141
+ def run(self, query: str) -> str:
142
+ return f"Result: {query}"
143
+
144
+ def _run(self, query: str) -> str:
145
+ return f"Result: {query}"
146
+
147
+ mock_tool = MockLangChainTool()
148
+
149
+ # 1. Error string mode (handle_tool_error=True)
150
+ guarded_lc = wrap_langchain_tool(mock_tool, engine=self.engine, handle_tool_error=True)
151
+ self.assertEqual(guarded_lc.name, "sql_db_query")
152
+
153
+ # Blocked query returns self-healing error string
154
+ blocked_res = guarded_lc.run(query="DROP TABLE users")
155
+ self.assertTrue("Error: [Aegis Policy Blocked] SQL-002" in blocked_res)
156
+
157
+ # Runnable .invoke() support
158
+ invoke_blocked = guarded_lc.invoke({"query": "DELETE FROM users"})
159
+ self.assertTrue("Error: [Aegis Policy Blocked] SQL-001" in invoke_blocked)
160
+
161
+ # Allowed query succeeds
162
+ allowed_res = guarded_lc.run("SELECT * FROM users WHERE id = 42")
163
+ self.assertEqual(allowed_res, "Result: SELECT * FROM users WHERE id = 42")
164
+
165
+ # 2. Strict exception mode (handle_tool_error=False)
166
+ strict_lc = AegisLangChainTool(mock_tool, engine=self.engine, handle_tool_error=False)
167
+ with self.assertRaises(AegisBlockedError):
168
+ strict_lc.run(query="DROP TABLE orders")
169
+
170
+ # 3. Async Runnable .ainvoke() support
171
+ import asyncio
172
+
173
+ class MockAsyncLangChainTool:
174
+ name = "async_sql"
175
+ async def ainvoke(self, input_dict: dict, config=None) -> str:
176
+ await asyncio.sleep(0.001)
177
+ return f"Async: {input_dict.get('query')}"
178
+
179
+ async_tool = MockAsyncLangChainTool()
180
+ guarded_async_lc = wrap_langchain_tool(async_tool, engine=self.engine)
181
+
182
+ async def run_async_tests():
183
+ # Blocked
184
+ res_b = await guarded_async_lc.ainvoke({"query": "DROP TABLE critical_data"})
185
+ self.assertTrue("Error: [Aegis Policy Blocked] SQL-002" in res_b)
186
+ # Allowed
187
+ res_a = await guarded_async_lc.ainvoke({"query": "SELECT count(*) FROM items WHERE status = 'active'"})
188
+ self.assertEqual(res_a, "Async: SELECT count(*) FROM items WHERE status = 'active'")
189
+
190
+ asyncio.run(run_async_tests())
191
+
192
+ def test_async_python_decorator(self):
193
+ import asyncio
194
+
195
+ @aegis_guard(tool_name="async_database_runner")
196
+ async def async_execute_db(query: str):
197
+ await asyncio.sleep(0.001)
198
+ return f"Async Executed: {query}"
199
+
200
+ # Should raise AegisBlockedError on rogue async call
201
+ async def run_blocked():
202
+ with self.assertRaises(AegisBlockedError):
203
+ await async_execute_db(query="DELETE FROM transactions")
204
+
205
+ # Should succeed on safe async call
206
+ async def run_allowed():
207
+ result = await async_execute_db(query="SELECT * FROM transactions WHERE id = 100")
208
+ self.assertEqual(result, "Async Executed: SELECT * FROM transactions WHERE id = 100")
209
+
210
+ asyncio.run(run_blocked())
211
+ asyncio.run(run_allowed())
212
+
213
+ def test_state_invariants(self):
214
+ # Cross-tenant mismatch test
215
+ rule_params = {"tenant_field": "tenantId"}
216
+ call = ToolCall(tool="update_profile", params={"tenantId": "tenant-attacker", "name": "Eve"})
217
+ violations = PythonStateChecker.evaluate("SOC2-004", "soc2-guard", rule_params, call, state={"tenantId": "tenant-legit"})
218
+ self.assertEqual(len(violations), 1)
219
+ self.assertEqual(violations[0].rule_id, "SOC2-004")
220
+
221
+ # Entity cancelled status test
222
+ rule_params2 = {"target_field": "order_id", "assertion": "state.order_status != 'cancelled'"}
223
+ call2 = ToolCall(tool="ship_order", params={"order_id": "ORD-999"})
224
+ violations2 = PythonStateChecker.evaluate("SOC2-005", "soc2-guard", rule_params2, call2, state={"order_status": "cancelled"})
225
+ self.assertEqual(len(violations2), 1)
226
+ self.assertEqual(violations2[0].rule_id, "SOC2-005")
227
+
228
+ def test_pii_token_vault(self):
229
+ vault = PythonPiiTokenVault(salt="test-fixed-salt")
230
+ raw_ssn = "123-45-6789"
231
+ token = vault.tokenize(raw_ssn, token_type="SSN")
232
+
233
+ self.assertTrue(token.startswith("<AEGIS_SSN_"))
234
+ self.assertNotIn("123-45-6789", token)
235
+
236
+ # Deterministic mapping within session
237
+ self.assertEqual(vault.tokenize(raw_ssn, token_type="SSN"), token)
238
+
239
+ # Detokenization
240
+ msg = f"User profile SSN is {token}"
241
+ restored = vault.detokenize(msg)
242
+ self.assertEqual(restored, "User profile SSN is 123-45-6789")
243
+
244
+ def test_zero_dependencies(self):
245
+ """Verify that aegis_kernel relies only on Python standard library."""
246
+ import aegis_kernel
247
+ import inspect
248
+
249
+ # Inspect all modules in aegis_kernel
250
+ for name in dir(aegis_kernel):
251
+ item = getattr(aegis_kernel, name)
252
+ if inspect.ismodule(item):
253
+ # Ensure no external third-party packages are loaded as package dependencies
254
+ mod_file = getattr(item, "__file__", "")
255
+ if mod_file:
256
+ self.assertTrue(
257
+ "site-packages" not in mod_file or "aegis" in mod_file,
258
+ f"Non-stdlib dependency detected: {item}"
259
+ )
260
+
261
+ def test_sub_100_microsecond_performance(self):
262
+ """Verify deterministic sub-0.1ms (< 100μs) evaluation latency."""
263
+ import time
264
+
265
+ call = ToolCall(tool="fast_eval", params={"query": "SELECT * FROM items WHERE id = 1"})
266
+
267
+ # Warmup
268
+ for _ in range(500):
269
+ self.engine.evaluate(call)
270
+
271
+ # Benchmark 5,000 iterations
272
+ N = 5000
273
+ start = time.perf_counter()
274
+ for _ in range(N):
275
+ self.engine.evaluate(call)
276
+ total_time_ms = (time.perf_counter() - start) * 1000.0
277
+ avg_latency_ms = total_time_ms / N
278
+
279
+ self.assertLess(
280
+ avg_latency_ms,
281
+ 0.10,
282
+ f"Average evaluation latency {avg_latency_ms:.4f} ms exceeded sub-0.1ms budget!"
283
+ )
284
+
285
+ if __name__ == "__main__":
286
+ unittest.main()
287
+
288
+