agent_os_kernel 3.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.
Files changed (337) hide show
  1. agent_control_plane/__init__.py +662 -0
  2. agent_control_plane/a2a_adapter.py +543 -0
  3. agent_control_plane/adapter.py +417 -0
  4. agent_control_plane/agent_hibernation.py +394 -0
  5. agent_control_plane/agent_kernel.py +470 -0
  6. agent_control_plane/compliance.py +720 -0
  7. agent_control_plane/constraint_graphs.py +478 -0
  8. agent_control_plane/control_plane.py +854 -0
  9. agent_control_plane/example_executors.py +195 -0
  10. agent_control_plane/execution_engine.py +231 -0
  11. agent_control_plane/flight_recorder.py +846 -0
  12. agent_control_plane/governance_layer.py +435 -0
  13. agent_control_plane/hf_utils.py +563 -0
  14. agent_control_plane/interfaces/__init__.py +55 -0
  15. agent_control_plane/interfaces/kernel_interface.py +361 -0
  16. agent_control_plane/interfaces/plugin_interface.py +497 -0
  17. agent_control_plane/interfaces/protocol_interfaces.py +387 -0
  18. agent_control_plane/kernel_space.py +1009 -0
  19. agent_control_plane/langchain_adapter.py +424 -0
  20. agent_control_plane/lifecycle.py +3113 -0
  21. agent_control_plane/mcp_adapter.py +653 -0
  22. agent_control_plane/ml_safety.py +563 -0
  23. agent_control_plane/multimodal.py +727 -0
  24. agent_control_plane/mute_agent.py +422 -0
  25. agent_control_plane/observability.py +787 -0
  26. agent_control_plane/orchestrator.py +482 -0
  27. agent_control_plane/plugin_registry.py +750 -0
  28. agent_control_plane/policy_engine.py +954 -0
  29. agent_control_plane/process_isolation.py +777 -0
  30. agent_control_plane/shadow_mode.py +310 -0
  31. agent_control_plane/signals.py +493 -0
  32. agent_control_plane/supervisor_agents.py +430 -0
  33. agent_control_plane/time_travel_debugger.py +557 -0
  34. agent_control_plane/tool_registry.py +452 -0
  35. agent_control_plane/vfs.py +697 -0
  36. agent_kernel/__init__.py +69 -0
  37. agent_kernel/analyzer.py +435 -0
  38. agent_kernel/auditor.py +36 -0
  39. agent_kernel/completeness_auditor.py +237 -0
  40. agent_kernel/detector.py +203 -0
  41. agent_kernel/kernel.py +744 -0
  42. agent_kernel/memory_manager.py +85 -0
  43. agent_kernel/models.py +374 -0
  44. agent_kernel/nudge_mechanism.py +263 -0
  45. agent_kernel/outcome_analyzer.py +338 -0
  46. agent_kernel/patcher.py +582 -0
  47. agent_kernel/semantic_analyzer.py +316 -0
  48. agent_kernel/semantic_purge.py +349 -0
  49. agent_kernel/simulator.py +449 -0
  50. agent_kernel/teacher.py +85 -0
  51. agent_kernel/triage.py +152 -0
  52. agent_os/__init__.py +409 -0
  53. agent_os/_adversarial_impl.py +200 -0
  54. agent_os/_circuit_breaker_impl.py +232 -0
  55. agent_os/_mcp_metrics.py +193 -0
  56. agent_os/adversarial.py +20 -0
  57. agent_os/agents_compat.py +490 -0
  58. agent_os/audit_logger.py +135 -0
  59. agent_os/base_agent.py +651 -0
  60. agent_os/circuit_breaker.py +34 -0
  61. agent_os/cli/__init__.py +659 -0
  62. agent_os/cli/cmd_audit.py +128 -0
  63. agent_os/cli/cmd_init.py +152 -0
  64. agent_os/cli/cmd_policy.py +41 -0
  65. agent_os/cli/cmd_policy_gen.py +180 -0
  66. agent_os/cli/cmd_validate.py +258 -0
  67. agent_os/cli/mcp_scan.py +265 -0
  68. agent_os/cli/output.py +192 -0
  69. agent_os/cli/policy_checker.py +330 -0
  70. agent_os/compat.py +74 -0
  71. agent_os/constraint_graph.py +234 -0
  72. agent_os/content_governance.py +140 -0
  73. agent_os/context_budget.py +305 -0
  74. agent_os/credential_redactor.py +224 -0
  75. agent_os/diff_policy.py +89 -0
  76. agent_os/egress_policy.py +159 -0
  77. agent_os/escalation.py +276 -0
  78. agent_os/event_bus.py +124 -0
  79. agent_os/exceptions.py +180 -0
  80. agent_os/execution_context_policy.py +141 -0
  81. agent_os/github_enterprise.py +96 -0
  82. agent_os/health.py +20 -0
  83. agent_os/integrations/__init__.py +279 -0
  84. agent_os/integrations/a2a_adapter.py +279 -0
  85. agent_os/integrations/agent_lightning/__init__.py +30 -0
  86. agent_os/integrations/anthropic_adapter.py +420 -0
  87. agent_os/integrations/autogen_adapter.py +620 -0
  88. agent_os/integrations/base.py +1137 -0
  89. agent_os/integrations/compat.py +229 -0
  90. agent_os/integrations/config.py +98 -0
  91. agent_os/integrations/conversation_guardian.py +957 -0
  92. agent_os/integrations/crewai_adapter.py +467 -0
  93. agent_os/integrations/drift_detector.py +425 -0
  94. agent_os/integrations/dry_run.py +124 -0
  95. agent_os/integrations/escalation.py +582 -0
  96. agent_os/integrations/gemini_adapter.py +364 -0
  97. agent_os/integrations/google_adk_adapter.py +633 -0
  98. agent_os/integrations/guardrails_adapter.py +394 -0
  99. agent_os/integrations/health.py +197 -0
  100. agent_os/integrations/langchain_adapter.py +654 -0
  101. agent_os/integrations/llamafirewall.py +343 -0
  102. agent_os/integrations/llamaindex_adapter.py +188 -0
  103. agent_os/integrations/logging.py +191 -0
  104. agent_os/integrations/maf_adapter.py +631 -0
  105. agent_os/integrations/mistral_adapter.py +365 -0
  106. agent_os/integrations/openai_adapter.py +816 -0
  107. agent_os/integrations/openai_agents_sdk.py +406 -0
  108. agent_os/integrations/policy_compose.py +171 -0
  109. agent_os/integrations/profiling.py +144 -0
  110. agent_os/integrations/pydantic_ai_adapter.py +420 -0
  111. agent_os/integrations/rate_limiter.py +130 -0
  112. agent_os/integrations/rbac.py +143 -0
  113. agent_os/integrations/registry.py +113 -0
  114. agent_os/integrations/scope_guard.py +303 -0
  115. agent_os/integrations/semantic_kernel_adapter.py +769 -0
  116. agent_os/integrations/smolagents_adapter.py +629 -0
  117. agent_os/integrations/templates.py +178 -0
  118. agent_os/integrations/token_budget.py +134 -0
  119. agent_os/integrations/tool_aliases.py +190 -0
  120. agent_os/integrations/webhooks.py +177 -0
  121. agent_os/lite.py +208 -0
  122. agent_os/mcp_gateway.py +385 -0
  123. agent_os/mcp_message_signer.py +273 -0
  124. agent_os/mcp_protocols.py +161 -0
  125. agent_os/mcp_response_scanner.py +232 -0
  126. agent_os/mcp_security.py +924 -0
  127. agent_os/mcp_session_auth.py +231 -0
  128. agent_os/mcp_sliding_rate_limiter.py +184 -0
  129. agent_os/memory_guard.py +409 -0
  130. agent_os/metrics.py +134 -0
  131. agent_os/mute.py +428 -0
  132. agent_os/mute_agent.py +209 -0
  133. agent_os/policies/__init__.py +77 -0
  134. agent_os/policies/async_evaluator.py +275 -0
  135. agent_os/policies/backends.py +670 -0
  136. agent_os/policies/bridge.py +169 -0
  137. agent_os/policies/budget.py +85 -0
  138. agent_os/policies/cli.py +294 -0
  139. agent_os/policies/conflict_resolution.py +270 -0
  140. agent_os/policies/data_classification.py +252 -0
  141. agent_os/policies/evaluator.py +239 -0
  142. agent_os/policies/policy_schema.json +228 -0
  143. agent_os/policies/rate_limiting.py +145 -0
  144. agent_os/policies/schema.py +115 -0
  145. agent_os/policies/shared.py +331 -0
  146. agent_os/prompt_injection.py +694 -0
  147. agent_os/providers.py +182 -0
  148. agent_os/py.typed +0 -0
  149. agent_os/retry.py +81 -0
  150. agent_os/reversibility.py +251 -0
  151. agent_os/sandbox.py +432 -0
  152. agent_os/sandbox_provider.py +140 -0
  153. agent_os/secure_codegen.py +525 -0
  154. agent_os/security_skills.py +538 -0
  155. agent_os/semantic_policy.py +422 -0
  156. agent_os/server/__init__.py +15 -0
  157. agent_os/server/__main__.py +25 -0
  158. agent_os/server/app.py +277 -0
  159. agent_os/server/models.py +104 -0
  160. agent_os/shift_left_metrics.py +130 -0
  161. agent_os/stateless.py +742 -0
  162. agent_os/supervisor.py +148 -0
  163. agent_os/task_outcome.py +148 -0
  164. agent_os/transparency.py +181 -0
  165. agent_os/trust_root.py +128 -0
  166. agent_os_kernel-3.1.0.dist-info/METADATA +1269 -0
  167. agent_os_kernel-3.1.0.dist-info/RECORD +337 -0
  168. agent_os_kernel-3.1.0.dist-info/WHEEL +4 -0
  169. agent_os_kernel-3.1.0.dist-info/entry_points.txt +2 -0
  170. agent_os_kernel-3.1.0.dist-info/licenses/LICENSE +21 -0
  171. agent_os_observability/__init__.py +27 -0
  172. agent_os_observability/dashboards.py +898 -0
  173. agent_os_observability/metrics.py +398 -0
  174. agent_os_observability/server.py +223 -0
  175. agent_os_observability/tracer.py +232 -0
  176. agent_primitives/__init__.py +24 -0
  177. agent_primitives/failures.py +84 -0
  178. agent_primitives/py.typed +0 -0
  179. amb_core/__init__.py +177 -0
  180. amb_core/adapters/__init__.py +57 -0
  181. amb_core/adapters/aws_sqs_broker.py +376 -0
  182. amb_core/adapters/azure_servicebus_broker.py +340 -0
  183. amb_core/adapters/kafka_broker.py +260 -0
  184. amb_core/adapters/nats_broker.py +285 -0
  185. amb_core/adapters/rabbitmq_broker.py +235 -0
  186. amb_core/adapters/redis_broker.py +262 -0
  187. amb_core/broker.py +145 -0
  188. amb_core/bus.py +481 -0
  189. amb_core/cloudevents.py +509 -0
  190. amb_core/dlq.py +345 -0
  191. amb_core/hf_utils.py +536 -0
  192. amb_core/memory_broker.py +410 -0
  193. amb_core/models.py +141 -0
  194. amb_core/persistence.py +529 -0
  195. amb_core/schema.py +294 -0
  196. amb_core/tracing.py +358 -0
  197. atr/__init__.py +640 -0
  198. atr/access.py +348 -0
  199. atr/composition.py +645 -0
  200. atr/decorator.py +357 -0
  201. atr/executor.py +384 -0
  202. atr/health.py +557 -0
  203. atr/hf_utils.py +449 -0
  204. atr/injection.py +422 -0
  205. atr/metrics.py +440 -0
  206. atr/policies.py +403 -0
  207. atr/py.typed +2 -0
  208. atr/registry.py +452 -0
  209. atr/schema.py +480 -0
  210. atr/tools/safe/__init__.py +75 -0
  211. atr/tools/safe/calculator.py +467 -0
  212. atr/tools/safe/datetime_tool.py +443 -0
  213. atr/tools/safe/file_reader.py +402 -0
  214. atr/tools/safe/http_client.py +316 -0
  215. atr/tools/safe/json_parser.py +374 -0
  216. atr/tools/safe/text_tool.py +537 -0
  217. atr/tools/safe/toolkit.py +175 -0
  218. caas/__init__.py +162 -0
  219. caas/api/__init__.py +7 -0
  220. caas/api/server.py +1328 -0
  221. caas/caching.py +834 -0
  222. caas/cli.py +210 -0
  223. caas/conversation.py +223 -0
  224. caas/decay.py +72 -0
  225. caas/detection/__init__.py +9 -0
  226. caas/detection/detector.py +238 -0
  227. caas/enrichment.py +130 -0
  228. caas/gateway/__init__.py +27 -0
  229. caas/gateway/trust_gateway.py +474 -0
  230. caas/hf_utils.py +479 -0
  231. caas/ingestion/__init__.py +23 -0
  232. caas/ingestion/processors.py +253 -0
  233. caas/ingestion/structure_parser.py +188 -0
  234. caas/models.py +356 -0
  235. caas/pragmatic_truth.py +444 -0
  236. caas/routing/__init__.py +10 -0
  237. caas/routing/heuristic_router.py +58 -0
  238. caas/storage/__init__.py +9 -0
  239. caas/storage/store.py +389 -0
  240. caas/triad.py +213 -0
  241. caas/tuning/__init__.py +9 -0
  242. caas/tuning/tuner.py +329 -0
  243. caas/vfs/__init__.py +14 -0
  244. caas/vfs/filesystem.py +452 -0
  245. cmvk/__init__.py +218 -0
  246. cmvk/audit.py +402 -0
  247. cmvk/benchmarks.py +478 -0
  248. cmvk/constitutional.py +904 -0
  249. cmvk/hf_utils.py +301 -0
  250. cmvk/metrics.py +473 -0
  251. cmvk/profiles.py +300 -0
  252. cmvk/py.typed +0 -0
  253. cmvk/types.py +12 -0
  254. cmvk/verification.py +956 -0
  255. emk/__init__.py +89 -0
  256. emk/causal.py +352 -0
  257. emk/hf_utils.py +421 -0
  258. emk/indexer.py +83 -0
  259. emk/py.typed +0 -0
  260. emk/schema.py +204 -0
  261. emk/sleep_cycle.py +347 -0
  262. emk/store.py +281 -0
  263. iatp/__init__.py +166 -0
  264. iatp/attestation.py +461 -0
  265. iatp/cli.py +317 -0
  266. iatp/hf_utils.py +472 -0
  267. iatp/ipc_pipes.py +580 -0
  268. iatp/main.py +412 -0
  269. iatp/models/__init__.py +447 -0
  270. iatp/policy_engine.py +337 -0
  271. iatp/py.typed +2 -0
  272. iatp/recovery.py +321 -0
  273. iatp/security/__init__.py +270 -0
  274. iatp/sidecar/__init__.py +519 -0
  275. iatp/telemetry/__init__.py +164 -0
  276. iatp/tests/__init__.py +1 -0
  277. iatp/tests/test_attestation.py +370 -0
  278. iatp/tests/test_cli.py +131 -0
  279. iatp/tests/test_ed25519_attestation.py +211 -0
  280. iatp/tests/test_models.py +130 -0
  281. iatp/tests/test_policy_engine.py +347 -0
  282. iatp/tests/test_recovery.py +281 -0
  283. iatp/tests/test_security.py +222 -0
  284. iatp/tests/test_sidecar.py +167 -0
  285. iatp/tests/test_telemetry.py +175 -0
  286. mcp_kernel_server/__init__.py +28 -0
  287. mcp_kernel_server/cli.py +274 -0
  288. mcp_kernel_server/resources.py +217 -0
  289. mcp_kernel_server/server.py +564 -0
  290. mcp_kernel_server/tools.py +1174 -0
  291. mute_agent/__init__.py +68 -0
  292. mute_agent/core/__init__.py +1 -0
  293. mute_agent/core/execution_agent.py +166 -0
  294. mute_agent/core/handshake_protocol.py +201 -0
  295. mute_agent/core/reasoning_agent.py +238 -0
  296. mute_agent/knowledge_graph/__init__.py +1 -0
  297. mute_agent/knowledge_graph/graph_elements.py +65 -0
  298. mute_agent/knowledge_graph/multidimensional_graph.py +170 -0
  299. mute_agent/knowledge_graph/subgraph.py +224 -0
  300. mute_agent/listener/__init__.py +43 -0
  301. mute_agent/listener/adapters/__init__.py +31 -0
  302. mute_agent/listener/adapters/base_adapter.py +189 -0
  303. mute_agent/listener/adapters/caas_adapter.py +344 -0
  304. mute_agent/listener/adapters/control_plane_adapter.py +436 -0
  305. mute_agent/listener/adapters/iatp_adapter.py +332 -0
  306. mute_agent/listener/adapters/scak_adapter.py +251 -0
  307. mute_agent/listener/listener.py +610 -0
  308. mute_agent/listener/state_observer.py +436 -0
  309. mute_agent/listener/threshold_config.py +313 -0
  310. mute_agent/super_system/__init__.py +1 -0
  311. mute_agent/super_system/router.py +204 -0
  312. mute_agent/visualization/__init__.py +10 -0
  313. mute_agent/visualization/graph_debugger.py +502 -0
  314. nexus/README.md +60 -0
  315. nexus/__init__.py +51 -0
  316. nexus/arbiter.py +359 -0
  317. nexus/client.py +466 -0
  318. nexus/dmz.py +444 -0
  319. nexus/escrow.py +430 -0
  320. nexus/exceptions.py +286 -0
  321. nexus/pyproject.toml +36 -0
  322. nexus/registry.py +393 -0
  323. nexus/reputation.py +425 -0
  324. nexus/schemas/__init__.py +51 -0
  325. nexus/schemas/compliance.py +276 -0
  326. nexus/schemas/escrow.py +251 -0
  327. nexus/schemas/manifest.py +225 -0
  328. nexus/schemas/receipt.py +208 -0
  329. nexus/tests/__init__.py +0 -0
  330. nexus/tests/conftest.py +146 -0
  331. nexus/tests/test_arbiter.py +192 -0
  332. nexus/tests/test_dmz.py +194 -0
  333. nexus/tests/test_escrow.py +276 -0
  334. nexus/tests/test_exceptions.py +225 -0
  335. nexus/tests/test_registry.py +232 -0
  336. nexus/tests/test_reputation.py +328 -0
  337. nexus/tests/test_schemas.py +295 -0
@@ -0,0 +1,478 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ """
5
+ Constraint Graphs - Multi-Dimensional Context
6
+
7
+ Context in an enterprise isn't flat; it's a graph. The Constraint Graph system
8
+ provides multi-dimensional constraints that act as the "physics" of the agent's world.
9
+
10
+ Three types of graphs:
11
+ 1. Data Graph: Tables, schemas, and data the agent can access
12
+ 2. Policy Graph: Corporate rules (e.g., "No PII in output")
13
+ 3. Temporal Graph: What is true RIGHT NOW (e.g., "Maintenance Window is Active")
14
+
15
+ If an agent tries to access something that exists in the Data Graph but is
16
+ blocked in the Policy Graph, the Control Plane intercepts it. The request
17
+ never even reaches the database.
18
+
19
+ Research Foundations:
20
+ - Context-aware access control from ABAC research (NIST SP 800-162)
21
+ - Multi-dimensional policy evaluation
22
+ - Graph-based constraint modeling for complex policy interactions
23
+ - Temporal logic for time-based constraints
24
+ - Privacy controls informed by "Privacy in Agentic Systems" (arXiv:2409.1087, 2024)
25
+
26
+ See docs/RESEARCH_FOUNDATION.md for complete references.
27
+ """
28
+
29
+ from typing import Any, Dict, List, Optional, Set, Callable, Tuple
30
+ from dataclasses import dataclass, field
31
+ from datetime import datetime, time
32
+ from enum import Enum
33
+ from .agent_kernel import ExecutionRequest, ActionType
34
+
35
+
36
+ class GraphNodeType(Enum):
37
+ """Types of nodes in constraint graphs"""
38
+ DATA_RESOURCE = "data_resource"
39
+ POLICY_RULE = "policy_rule"
40
+ TEMPORAL_CONSTRAINT = "temporal_constraint"
41
+ AGENT_ROLE = "agent_role"
42
+
43
+
44
+ @dataclass
45
+ class GraphNode:
46
+ """A node in a constraint graph"""
47
+ node_id: str
48
+ node_type: GraphNodeType
49
+ name: str
50
+ properties: Dict[str, Any] = field(default_factory=dict)
51
+ metadata: Dict[str, Any] = field(default_factory=dict)
52
+
53
+
54
+ @dataclass
55
+ class GraphEdge:
56
+ """An edge connecting nodes in a constraint graph"""
57
+ from_node: str
58
+ to_node: str
59
+ edge_type: str # e.g., "blocks", "allows", "requires", "inherits"
60
+ properties: Dict[str, Any] = field(default_factory=dict)
61
+
62
+
63
+ class ConstraintGraph:
64
+ """Base class for constraint graphs"""
65
+
66
+ def __init__(self, name: str):
67
+ self.name = name
68
+ self.nodes: Dict[str, GraphNode] = {}
69
+ self.edges: List[GraphEdge] = []
70
+
71
+ def add_node(self, node: GraphNode):
72
+ """Add a node to the graph"""
73
+ self.nodes[node.node_id] = node
74
+
75
+ def add_edge(self, edge: GraphEdge):
76
+ """Add an edge to the graph"""
77
+ self.edges.append(edge)
78
+
79
+ def get_node(self, node_id: str) -> Optional[GraphNode]:
80
+ """Get a node by ID"""
81
+ return self.nodes.get(node_id)
82
+
83
+ def get_edges_from(self, node_id: str) -> List[GraphEdge]:
84
+ """Get all edges originating from a node"""
85
+ return [e for e in self.edges if e.from_node == node_id]
86
+
87
+ def get_edges_to(self, node_id: str) -> List[GraphEdge]:
88
+ """Get all edges pointing to a node"""
89
+ return [e for e in self.edges if e.to_node == node_id]
90
+
91
+ def is_allowed(self, from_node: str, to_node: str) -> bool:
92
+ """Check if there's an 'allows' edge between nodes"""
93
+ return any(
94
+ e.from_node == from_node and e.to_node == to_node and e.edge_type == "allows"
95
+ for e in self.edges
96
+ )
97
+
98
+ def is_blocked(self, from_node: str, to_node: str) -> bool:
99
+ """Check if there's a 'blocks' edge between nodes"""
100
+ return any(
101
+ e.from_node == from_node and e.to_node == to_node and e.edge_type == "blocks"
102
+ for e in self.edges
103
+ )
104
+
105
+
106
+ class DataGraph(ConstraintGraph):
107
+ """
108
+ Data Graph - Defines what data resources exist and can be accessed.
109
+
110
+ Examples:
111
+ - Database tables and schemas
112
+ - File systems and directories
113
+ - API endpoints
114
+ - Data lakes and warehouses
115
+ """
116
+
117
+ def __init__(self):
118
+ super().__init__("DataGraph")
119
+
120
+ def add_database_table(self, table_name: str, schema: Dict[str, Any], metadata: Optional[Dict] = None):
121
+ """Add a database table to the graph"""
122
+ node = GraphNode(
123
+ node_id=f"table:{table_name}",
124
+ node_type=GraphNodeType.DATA_RESOURCE,
125
+ name=table_name,
126
+ properties={"schema": schema, "resource_type": "database_table"},
127
+ metadata=metadata or {}
128
+ )
129
+ self.add_node(node)
130
+
131
+ def add_file_path(self, path: str, access_level: str = "read", metadata: Optional[Dict] = None):
132
+ """Add a file path to the graph"""
133
+ node = GraphNode(
134
+ node_id=f"file:{path}",
135
+ node_type=GraphNodeType.DATA_RESOURCE,
136
+ name=path,
137
+ properties={"resource_type": "file", "access_level": access_level},
138
+ metadata=metadata or {}
139
+ )
140
+ self.add_node(node)
141
+
142
+ def add_api_endpoint(self, endpoint: str, methods: List[str], metadata: Optional[Dict] = None):
143
+ """Add an API endpoint to the graph"""
144
+ node = GraphNode(
145
+ node_id=f"api:{endpoint}",
146
+ node_type=GraphNodeType.DATA_RESOURCE,
147
+ name=endpoint,
148
+ properties={"resource_type": "api", "methods": methods},
149
+ metadata=metadata or {}
150
+ )
151
+ self.add_node(node)
152
+
153
+ def get_accessible_tables(self) -> List[str]:
154
+ """Get all accessible database tables"""
155
+ return [
156
+ node.name for node in self.nodes.values()
157
+ if node.properties.get("resource_type") == "database_table"
158
+ ]
159
+
160
+ def get_accessible_paths(self) -> List[str]:
161
+ """Get all accessible file paths"""
162
+ return [
163
+ node.name for node in self.nodes.values()
164
+ if node.properties.get("resource_type") == "file"
165
+ ]
166
+
167
+
168
+ class PolicyGraph(ConstraintGraph):
169
+ """
170
+ Policy Graph - Defines corporate rules and compliance constraints.
171
+
172
+ Examples:
173
+ - "No PII in output"
174
+ - "Finance data requires approval"
175
+ - "Healthcare data is HIPAA protected"
176
+ - "Cannot access production during deployment"
177
+ """
178
+
179
+ def __init__(self):
180
+ super().__init__("PolicyGraph")
181
+
182
+ def add_policy_rule(
183
+ self,
184
+ rule_id: str,
185
+ name: str,
186
+ applies_to: List[str], # Node IDs this rule applies to
187
+ rule_type: str, # "allow", "deny", "require_approval"
188
+ validator: Optional[Callable] = None
189
+ ):
190
+ """Add a policy rule to the graph"""
191
+ node = GraphNode(
192
+ node_id=f"policy:{rule_id}",
193
+ node_type=GraphNodeType.POLICY_RULE,
194
+ name=name,
195
+ properties={
196
+ "rule_type": rule_type,
197
+ "applies_to": applies_to,
198
+ "validator": validator
199
+ }
200
+ )
201
+ self.add_node(node)
202
+
203
+ # Create edges to resources this policy applies to
204
+ for resource_id in applies_to:
205
+ edge = GraphEdge(
206
+ from_node=node.node_id,
207
+ to_node=resource_id,
208
+ edge_type=rule_type
209
+ )
210
+ self.add_edge(edge)
211
+
212
+ def add_pii_protection(self, resource_ids: List[str]):
213
+ """Add PII protection policy to resources"""
214
+ self.add_policy_rule(
215
+ rule_id="pii_protection",
216
+ name="No PII in output",
217
+ applies_to=resource_ids,
218
+ rule_type="deny",
219
+ validator=lambda req: not self._contains_pii(req.parameters)
220
+ )
221
+
222
+ def add_approval_requirement(self, resource_ids: List[str], approver_role: str):
223
+ """Add approval requirement for resources"""
224
+ self.add_policy_rule(
225
+ rule_id=f"require_approval_{approver_role}",
226
+ name=f"Requires approval from {approver_role}",
227
+ applies_to=resource_ids,
228
+ rule_type="require_approval",
229
+ )
230
+
231
+ @staticmethod
232
+ def _contains_pii(parameters: Dict[str, Any]) -> bool:
233
+ """Check if parameters might contain PII"""
234
+ pii_keywords = ['ssn', 'social_security', 'email', 'phone', 'address', 'credit_card']
235
+ params_str = str(parameters).lower()
236
+ return any(keyword in params_str for keyword in pii_keywords)
237
+
238
+ def check_policy_violations(self, agent_role: str, resource_id: str) -> List[str]:
239
+ """Check if accessing a resource would violate policies"""
240
+ violations = []
241
+
242
+ # Find policies that apply to this resource
243
+ for edge in self.get_edges_to(resource_id):
244
+ if edge.edge_type == "deny":
245
+ policy_node = self.get_node(edge.from_node)
246
+ violations.append(f"Policy '{policy_node.name}' blocks access to resource '{resource_id}' for role '{agent_role}'")
247
+
248
+ return violations
249
+
250
+
251
+ class TemporalGraph(ConstraintGraph):
252
+ """
253
+ Temporal Graph - Defines what is true RIGHT NOW.
254
+
255
+ Examples:
256
+ - "Maintenance Window is Active (no writes)"
257
+ - "Business hours (9-5 EST)"
258
+ - "End of quarter freeze period"
259
+ - "Peak traffic hours (throttle)"
260
+ """
261
+
262
+ def __init__(self):
263
+ super().__init__("TemporalGraph")
264
+ self.time_constraints: Dict[str, Callable[[], bool]] = {}
265
+
266
+ def add_maintenance_window(
267
+ self,
268
+ window_id: str,
269
+ start_time: time,
270
+ end_time: time,
271
+ blocked_actions: List[ActionType]
272
+ ):
273
+ """Add a maintenance window constraint"""
274
+ node = GraphNode(
275
+ node_id=f"temporal:{window_id}",
276
+ node_type=GraphNodeType.TEMPORAL_CONSTRAINT,
277
+ name=f"Maintenance Window: {start_time}-{end_time}",
278
+ properties={
279
+ "start_time": start_time,
280
+ "end_time": end_time,
281
+ "blocked_actions": blocked_actions
282
+ }
283
+ )
284
+ self.add_node(node)
285
+
286
+ # Add constraint checker
287
+ self.time_constraints[window_id] = lambda: self._is_in_time_range(start_time, end_time)
288
+
289
+ def add_business_hours(
290
+ self,
291
+ hours_id: str,
292
+ start_time: time,
293
+ end_time: time,
294
+ required_for: List[ActionType]
295
+ ):
296
+ """Add business hours constraint"""
297
+ node = GraphNode(
298
+ node_id=f"temporal:{hours_id}",
299
+ node_type=GraphNodeType.TEMPORAL_CONSTRAINT,
300
+ name=f"Business Hours: {start_time}-{end_time}",
301
+ properties={
302
+ "start_time": start_time,
303
+ "end_time": end_time,
304
+ "required_for": required_for
305
+ }
306
+ )
307
+ self.add_node(node)
308
+
309
+ self.time_constraints[hours_id] = lambda: self._is_in_time_range(start_time, end_time)
310
+
311
+ def add_freeze_period(
312
+ self,
313
+ freeze_id: str,
314
+ start_date: datetime,
315
+ end_date: datetime,
316
+ reason: str
317
+ ):
318
+ """Add a freeze period (e.g., end of quarter, holidays)"""
319
+ node = GraphNode(
320
+ node_id=f"temporal:{freeze_id}",
321
+ node_type=GraphNodeType.TEMPORAL_CONSTRAINT,
322
+ name=f"Freeze Period: {reason}",
323
+ properties={
324
+ "start_date": start_date,
325
+ "end_date": end_date,
326
+ "reason": reason
327
+ }
328
+ )
329
+ self.add_node(node)
330
+
331
+ self.time_constraints[freeze_id] = lambda: self._is_in_date_range(start_date, end_date)
332
+
333
+ @staticmethod
334
+ def _is_in_time_range(start_time: time, end_time: time) -> bool:
335
+ """Check if current time is within range"""
336
+ now = datetime.now().time()
337
+ if start_time <= end_time:
338
+ # Normal range (e.g., 9:00 to 17:00)
339
+ return start_time <= now <= end_time
340
+ else:
341
+ # Crosses midnight (e.g., 23:00 to 01:00)
342
+ return now >= start_time or now <= end_time
343
+
344
+ @staticmethod
345
+ def _is_in_date_range(start_date: datetime, end_date: datetime) -> bool:
346
+ """Check if current date is within range"""
347
+ now = datetime.now()
348
+ return start_date <= now <= end_date
349
+
350
+ def is_action_allowed_now(self, action_type: ActionType) -> Tuple[bool, Optional[str]]:
351
+ """Check if an action is allowed at the current time"""
352
+ for node in self.nodes.values():
353
+ if node.node_type == GraphNodeType.TEMPORAL_CONSTRAINT:
354
+ # Check maintenance windows
355
+ blocked_actions = node.properties.get("blocked_actions", [])
356
+ if action_type in blocked_actions:
357
+ constraint_id = node.node_id.split(":")[-1]
358
+ if self.time_constraints.get(constraint_id, lambda: False)():
359
+ return False, f"Action blocked by: {node.name}"
360
+
361
+ # Check business hours requirements
362
+ required_for = node.properties.get("required_for", [])
363
+ if action_type in required_for:
364
+ constraint_id = node.node_id.split(":")[-1]
365
+ if not self.time_constraints.get(constraint_id, lambda: True)():
366
+ return False, f"Action requires: {node.name}"
367
+
368
+ return True, None
369
+
370
+
371
+ class ConstraintGraphValidator:
372
+ """
373
+ Validates requests against multi-dimensional constraint graphs.
374
+
375
+ This is deterministic enforcement. The LLM can "think" whatever it wants,
376
+ but it can only ACT on what the graphs permit.
377
+ """
378
+
379
+ def __init__(
380
+ self,
381
+ data_graph: DataGraph,
382
+ policy_graph: PolicyGraph,
383
+ temporal_graph: TemporalGraph
384
+ ):
385
+ self.data_graph = data_graph
386
+ self.policy_graph = policy_graph
387
+ self.temporal_graph = temporal_graph
388
+ self.validation_log: List[Dict[str, Any]] = []
389
+
390
+ def validate_request(self, request: ExecutionRequest) -> Tuple[bool, List[str]]:
391
+ """
392
+ Validate request against all constraint graphs.
393
+
394
+ Returns:
395
+ (is_valid, reasons_if_invalid)
396
+ """
397
+ violations = []
398
+
399
+ # 1. Check Data Graph - does the resource exist and is it accessible?
400
+ data_valid, data_reason = self._validate_data_graph(request)
401
+ if not data_valid:
402
+ violations.append(f"Data Graph: {data_reason}")
403
+
404
+ # 2. Check Policy Graph - does this violate any policies?
405
+ policy_valid, policy_reasons = self._validate_policy_graph(request)
406
+ if not policy_valid:
407
+ violations.extend([f"Policy Graph: {r}" for r in policy_reasons])
408
+
409
+ # 3. Check Temporal Graph - is this allowed right now?
410
+ temporal_valid, temporal_reason = self._validate_temporal_graph(request)
411
+ if not temporal_valid:
412
+ violations.append(f"Temporal Graph: {temporal_reason}")
413
+
414
+ # Log validation
415
+ self.validation_log.append({
416
+ "request_id": request.request_id,
417
+ "timestamp": datetime.now().isoformat(),
418
+ "valid": len(violations) == 0,
419
+ "violations": violations
420
+ })
421
+
422
+ return len(violations) == 0, violations
423
+
424
+ def _validate_data_graph(self, request: ExecutionRequest) -> Tuple[bool, Optional[str]]:
425
+ """Validate against data graph"""
426
+ # Check if accessing a database table
427
+ if request.action_type in [ActionType.DATABASE_QUERY, ActionType.DATABASE_WRITE]:
428
+ table = request.parameters.get('table', request.parameters.get('database'))
429
+ if table:
430
+ node = self.data_graph.get_node(f"table:{table}")
431
+ if not node:
432
+ return False, f"Table '{table}' not in accessible data graph"
433
+
434
+ # Check if accessing a file
435
+ elif request.action_type in [ActionType.FILE_READ, ActionType.FILE_WRITE]:
436
+ path = request.parameters.get('path')
437
+ if path:
438
+ # Check if path is in data graph
439
+ accessible_paths = self.data_graph.get_accessible_paths()
440
+ if not any(path.startswith(p) for p in accessible_paths):
441
+ return False, f"Path '{path}' not in accessible data graph"
442
+
443
+ return True, None
444
+
445
+ def _validate_policy_graph(self, request: ExecutionRequest) -> Tuple[bool, List[str]]:
446
+ """Validate against policy graph"""
447
+ violations = []
448
+
449
+ # Build resource ID based on action type
450
+ resource_id = self._build_resource_id(request)
451
+ if resource_id:
452
+ policy_violations = self.policy_graph.check_policy_violations(
453
+ request.agent_context.agent_id,
454
+ resource_id
455
+ )
456
+ violations.extend(policy_violations)
457
+
458
+ return len(violations) == 0, violations
459
+
460
+ def _validate_temporal_graph(self, request: ExecutionRequest) -> Tuple[bool, Optional[str]]:
461
+ """Validate against temporal graph"""
462
+ allowed, reason = self.temporal_graph.is_action_allowed_now(request.action_type)
463
+ return allowed, reason
464
+
465
+ @staticmethod
466
+ def _build_resource_id(request: ExecutionRequest) -> Optional[str]:
467
+ """Build a resource ID from the request"""
468
+ if request.action_type in [ActionType.DATABASE_QUERY, ActionType.DATABASE_WRITE]:
469
+ table = request.parameters.get('table', request.parameters.get('database'))
470
+ return f"table:{table}" if table else None
471
+ elif request.action_type in [ActionType.FILE_READ, ActionType.FILE_WRITE]:
472
+ path = request.parameters.get('path')
473
+ return f"file:{path}" if path else None
474
+ return None
475
+
476
+ def get_validation_log(self) -> List[Dict[str, Any]]:
477
+ """Get validation log"""
478
+ return self.validation_log.copy()