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
iatp/policy_engine.py ADDED
@@ -0,0 +1,337 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """
4
+ IATP Policy Engine - Protocol Layer Policy Validation.
5
+
6
+ This module provides policy validation for IATP capability manifests
7
+ using a standalone, protocol-native implementation.
8
+
9
+ Design Note: IATP is Layer 2 (Infrastructure/Protocol). It defines the
10
+ message format, handshake protocols, and trust scores. Higher layers
11
+ (like agent-control-plane) USE this protocol; this protocol does NOT
12
+ depend on them.
13
+ """
14
+ from typing import Any, Dict, List, Optional, Protocol, Tuple, runtime_checkable
15
+
16
+ from iatp.models import CapabilityManifest, ReversibilityLevel
17
+
18
+
19
+ @runtime_checkable
20
+ class PolicyEvaluator(Protocol):
21
+ """
22
+ Protocol class for policy evaluation.
23
+
24
+ This allows duck typing for any policy evaluator implementation
25
+ without requiring a specific import.
26
+ """
27
+
28
+ def evaluate(self, context: Dict[str, Any]) -> str:
29
+ """Evaluate context and return action: 'allow', 'warn', or 'deny'."""
30
+ ...
31
+
32
+
33
+ class PolicyRule:
34
+ """
35
+ A single policy rule for manifest validation.
36
+
37
+ Rules are evaluated in order and the first matching rule determines
38
+ the action. Actions can be 'allow', 'warn', or 'deny'.
39
+ """
40
+
41
+ def __init__(
42
+ self,
43
+ name: str,
44
+ action: str,
45
+ conditions: Dict[str, List[Any]],
46
+ description: str = ""
47
+ ):
48
+ """
49
+ Initialize a policy rule.
50
+
51
+ Args:
52
+ name: Rule identifier
53
+ action: Action to take ('allow', 'warn', 'deny')
54
+ conditions: Dictionary mapping field names to allowed values
55
+ description: Human-readable description
56
+ """
57
+ self.name = name
58
+ self.action = action
59
+ self.conditions = conditions
60
+ self.description = description
61
+
62
+ def matches(self, context: Dict[str, Any]) -> bool:
63
+ """
64
+ Check if this rule matches the given context.
65
+
66
+ Args:
67
+ context: Policy evaluation context
68
+
69
+ Returns:
70
+ True if any condition matches
71
+ """
72
+ for key, values in self.conditions.items():
73
+ if key in context and context[key] in values:
74
+ return True
75
+ return False
76
+
77
+
78
+ class IATPPolicyEngine:
79
+ """
80
+ IATP Policy Engine for capability manifest validation.
81
+
82
+ This is a protocol-native policy engine that validates incoming
83
+ agent manifests against configurable policy rules. It uses the
84
+ IATP trust scoring algorithm and provides warn/block decisions.
85
+
86
+ The engine is designed to be:
87
+ - Self-contained (no external dependencies beyond IATP models)
88
+ - Extensible (custom rules can be added)
89
+ - Protocol-compliant (follows IATP trust semantics)
90
+ """
91
+
92
+ def __init__(self):
93
+ """Initialize the IATP Policy Engine."""
94
+ self.rules: List[PolicyRule] = []
95
+ self._setup_default_policies()
96
+
97
+ def _setup_default_policies(self):
98
+ """Setup default security policies for IATP."""
99
+ # Default IATP policy rules
100
+ # These augment (not replace) the SecurityValidator checks
101
+ self.rules = [
102
+ PolicyRule(
103
+ name="WarnUntrustedAgent",
104
+ description="Warn when agents are marked as untrusted",
105
+ action="warn",
106
+ conditions={"trust_level": ["untrusted"]}
107
+ ),
108
+ PolicyRule(
109
+ name="RequireReversibility",
110
+ description="Warn when agents don't support reversibility",
111
+ action="warn",
112
+ conditions={"reversibility": ["none"]}
113
+ ),
114
+ PolicyRule(
115
+ name="AllowEphemeral",
116
+ description="Allow agents with ephemeral data retention",
117
+ action="allow",
118
+ conditions={"retention_policy": ["ephemeral"]}
119
+ ),
120
+ ]
121
+
122
+ def add_custom_rule(self, rule: Dict[str, Any]):
123
+ """
124
+ Add a custom policy rule.
125
+
126
+ Args:
127
+ rule: Dictionary defining the policy rule with keys:
128
+ - name: Rule name
129
+ - description: Rule description (optional)
130
+ - action: "allow", "warn", or "deny"
131
+ - conditions: Dictionary of conditions to match
132
+ """
133
+ policy_rule = PolicyRule(
134
+ name=rule.get("name", "CustomRule"),
135
+ action=rule.get("action", "warn"),
136
+ conditions=rule.get("conditions", {}),
137
+ description=rule.get("description", "")
138
+ )
139
+ # Insert at beginning so custom rules take precedence
140
+ self.rules.insert(0, policy_rule)
141
+
142
+ def validate_manifest(
143
+ self,
144
+ manifest: CapabilityManifest
145
+ ) -> Tuple[bool, Optional[str], Optional[str]]:
146
+ """
147
+ Validate a capability manifest against policies.
148
+
149
+ This is the main integration point that validates incoming
150
+ agent manifests against the configured policy rules.
151
+
152
+ Args:
153
+ manifest: The capability manifest to validate
154
+
155
+ Returns:
156
+ Tuple of (is_allowed, error_message, warning_message)
157
+ - is_allowed: True if request should proceed
158
+ - error_message: Blocking error if is_allowed is False
159
+ - warning_message: Warning for user if there are concerns
160
+ """
161
+ # Convert manifest to policy context
162
+ context = self._manifest_to_context(manifest)
163
+
164
+ # Evaluate against policy rules
165
+ action = self._evaluate_rules(context)
166
+
167
+ # Generate appropriate response
168
+ if action == "deny":
169
+ return False, self._generate_deny_message(manifest, context), None
170
+ elif action == "warn":
171
+ return True, None, self._generate_warn_message(manifest, context)
172
+ else: # allow
173
+ return True, None, None
174
+
175
+ def _evaluate_rules(self, context: Dict[str, Any]) -> str:
176
+ """
177
+ Evaluate policy rules against context.
178
+
179
+ Args:
180
+ context: Policy evaluation context
181
+
182
+ Returns:
183
+ Action string: "allow", "warn", or "deny"
184
+ """
185
+ for rule in self.rules:
186
+ if rule.matches(context):
187
+ return rule.action
188
+
189
+ # Default to allow if no rules match
190
+ return "allow"
191
+
192
+ def _manifest_to_context(self, manifest: CapabilityManifest) -> Dict[str, Any]:
193
+ """
194
+ Convert a capability manifest to a policy evaluation context.
195
+
196
+ Args:
197
+ manifest: The capability manifest
198
+
199
+ Returns:
200
+ Dictionary context for policy evaluation
201
+ """
202
+ return {
203
+ "agent_id": manifest.agent_id,
204
+ "trust_level": manifest.trust_level.value,
205
+ "retention_policy": manifest.privacy_contract.retention.value,
206
+ "reversibility": manifest.capabilities.reversibility.value,
207
+ "idempotency": manifest.capabilities.idempotency,
208
+ "human_review": manifest.privacy_contract.human_review,
209
+ "encryption_at_rest": manifest.privacy_contract.encryption_at_rest,
210
+ "encryption_in_transit": manifest.privacy_contract.encryption_in_transit,
211
+ "scopes": manifest.scopes,
212
+ }
213
+
214
+ def _generate_deny_message(
215
+ self,
216
+ manifest: CapabilityManifest,
217
+ context: Dict[str, Any]
218
+ ) -> str:
219
+ """
220
+ Generate a denial message for blocked requests.
221
+
222
+ Args:
223
+ manifest: The capability manifest
224
+ context: Policy context
225
+
226
+ Returns:
227
+ User-friendly error message
228
+ """
229
+ reasons = []
230
+
231
+ if context["retention_policy"] in ["permanent", "forever"]:
232
+ reasons.append(
233
+ f"Agent '{manifest.agent_id}' stores data permanently, "
234
+ "which violates privacy policies"
235
+ )
236
+
237
+ if context["trust_level"] == "untrusted":
238
+ reasons.append(
239
+ f"Agent '{manifest.agent_id}' is marked as untrusted"
240
+ )
241
+
242
+ if reasons:
243
+ return "Policy Violation: " + "; ".join(reasons)
244
+
245
+ return f"Policy Violation: Agent '{manifest.agent_id}' failed policy validation"
246
+
247
+ def _generate_warn_message(
248
+ self,
249
+ manifest: CapabilityManifest,
250
+ context: Dict[str, Any]
251
+ ) -> str:
252
+ """
253
+ Generate a warning message for risky requests.
254
+
255
+ Args:
256
+ manifest: The capability manifest
257
+ context: Policy context
258
+
259
+ Returns:
260
+ User-friendly warning message
261
+ """
262
+ warnings = []
263
+
264
+ if context["reversibility"] == "none":
265
+ warnings.append(
266
+ f"Agent '{manifest.agent_id}' does not support transaction reversal"
267
+ )
268
+
269
+ if not context["idempotency"]:
270
+ warnings.append(
271
+ f"Agent '{manifest.agent_id}' may not handle duplicate requests safely"
272
+ )
273
+
274
+ if context["human_review"]:
275
+ warnings.append(
276
+ f"Agent '{manifest.agent_id}' may have humans review your data"
277
+ )
278
+
279
+ if warnings:
280
+ return "⚠️ Policy Warning:\n" + "\n".join(f" • {w}" for w in warnings)
281
+
282
+ return None
283
+
284
+ def validate_handshake(
285
+ self,
286
+ manifest: CapabilityManifest,
287
+ required_capabilities: Optional[List[str]] = None,
288
+ required_scopes: Optional[List[str]] = None
289
+ ) -> Tuple[bool, Optional[str]]:
290
+ """
291
+ Validate handshake compatibility between agents.
292
+
293
+ This checks if the remote agent's capabilities meet the
294
+ local agent's requirements.
295
+
296
+ Args:
297
+ manifest: Remote agent's capability manifest
298
+ required_capabilities: List of required capability keys
299
+ required_scopes: List of required RBAC scopes (e.g., ['repo:write'])
300
+
301
+ Returns:
302
+ Tuple of (is_compatible, error_message)
303
+ """
304
+ if not required_capabilities:
305
+ required_capabilities = []
306
+ if not required_scopes:
307
+ required_scopes = []
308
+
309
+ # Always validate against base policies first
310
+ is_allowed, error_msg, _ = self.validate_manifest(manifest)
311
+ if not is_allowed:
312
+ return False, error_msg
313
+
314
+ # Check specific capability requirements
315
+ missing = []
316
+ for capability in required_capabilities:
317
+ if capability == "reversibility" and \
318
+ manifest.capabilities.reversibility == ReversibilityLevel.NONE:
319
+ missing.append("reversibility support")
320
+ elif capability == "idempotency" and \
321
+ not manifest.capabilities.idempotency:
322
+ missing.append("idempotency support")
323
+
324
+ if missing:
325
+ return False, f"Agent missing required capabilities: {', '.join(missing)}"
326
+
327
+ # Check RBAC scope requirements
328
+ missing_scopes = []
329
+ agent_scopes = set(manifest.scopes)
330
+ for scope in required_scopes:
331
+ if scope not in agent_scopes:
332
+ missing_scopes.append(scope)
333
+
334
+ if missing_scopes:
335
+ return False, f"Agent missing required scopes: {', '.join(missing_scopes)}"
336
+
337
+ return True, None
iatp/py.typed ADDED
@@ -0,0 +1,2 @@
1
+ # PEP 561 marker file - indicates this package supports type checking
2
+ # https://peps.python.org/pep-0561/
iatp/recovery.py ADDED
@@ -0,0 +1,321 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """
4
+ Recovery Engine Integration with SCAK (Self-Correcting Agent Kernel).
5
+
6
+ This module wraps the agent_kernel (scak) to provide failure recovery
7
+ and rollback capabilities for IATP.
8
+ """
9
+ import asyncio
10
+ import logging
11
+ from datetime import datetime, timezone
12
+ from enum import Enum
13
+ from typing import Any, Callable, Dict, Optional
14
+
15
+ from agent_primitives import (
16
+ AgentFailure,
17
+ FailureType,
18
+ FailureSeverity,
19
+ )
20
+
21
+ from iatp.models import CapabilityManifest
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ class RecoveryStrategy(str, Enum):
27
+ """Recovery strategies for IATP."""
28
+ ROLLBACK = "rollback" # Execute compensation transaction
29
+ RETRY = "retry" # Retry the operation
30
+ GIVE_UP = "give_up" # No recovery possible
31
+
32
+
33
+ class IATPRecoveryEngine:
34
+ """
35
+ Wrapper around SCAK for IATP failure recovery.
36
+
37
+ This integrates the Self-Correcting Agent Kernel (scak) to provide
38
+ automatic failure detection, triage, and recovery for agent interactions.
39
+ """
40
+
41
+ def __init__(self):
42
+ """Initialize the IATP Recovery Engine."""
43
+ # Note: SelfCorrectingAgentKernel requires specific initialization
44
+ # We'll use the triage and diagnosis functions directly
45
+ self.recovery_history: Dict[str, Any] = {}
46
+
47
+ async def handle_failure(
48
+ self,
49
+ trace_id: str,
50
+ error: Exception,
51
+ manifest: CapabilityManifest,
52
+ payload: Dict[str, Any],
53
+ compensation_callback: Optional[Callable] = None
54
+ ) -> Dict[str, Any]:
55
+ """
56
+ Handle a failure in agent communication.
57
+
58
+ This is the main entry point for failure recovery. It:
59
+ 1. Diagnoses the failure using scak
60
+ 2. Determines the appropriate recovery strategy
61
+ 3. Executes compensation if available
62
+
63
+ Args:
64
+ trace_id: Unique trace ID for this request
65
+ error: The exception that occurred
66
+ manifest: The agent's capability manifest
67
+ payload: The original request payload
68
+ compensation_callback: Optional callback for rollback
69
+
70
+ Returns:
71
+ Dictionary with recovery result and actions taken
72
+ """
73
+ logger.info(f"[Recovery] Handling failure for trace {trace_id}")
74
+
75
+ # Determine failure type from exception
76
+ error_name = type(error).__name__
77
+ if "Timeout" in error_name:
78
+ failure_type = FailureType.TIMEOUT
79
+ elif "Resource" in error_name or "Limit" in error_name:
80
+ failure_type = FailureType.RESOURCE_EXHAUSTED
81
+ elif "Invalid" in error_name or "Validation" in error_name:
82
+ failure_type = FailureType.INVALID_ACTION
83
+ else:
84
+ failure_type = FailureType.UNKNOWN
85
+
86
+ # Create failure record using scak's AgentFailure model
87
+ failure = AgentFailure(
88
+ agent_id=manifest.agent_id,
89
+ failure_type=failure_type,
90
+ error_message=str(error),
91
+ context={
92
+ "trace_id": trace_id,
93
+ "payload": payload,
94
+ "reversibility": manifest.capabilities.reversibility.value,
95
+ },
96
+ timestamp=datetime.now(timezone.utc)
97
+ )
98
+
99
+ # Determine recovery strategy based on manifest and failure type
100
+ # Note: scak's diagnose_failure requires complex tool traces,
101
+ # so we use a simplified diagnosis based on the failure record
102
+ diagnosis = f"{failure_type.value}: {error_name}"
103
+
104
+ strategy = self._determine_strategy(
105
+ diagnosis,
106
+ manifest,
107
+ compensation_callback is not None
108
+ )
109
+
110
+ # Execute recovery
111
+ recovery_result = await self._execute_recovery(
112
+ strategy=strategy,
113
+ trace_id=trace_id,
114
+ failure=failure,
115
+ manifest=manifest,
116
+ compensation_callback=compensation_callback
117
+ )
118
+
119
+ # Record in history
120
+ self.recovery_history[trace_id] = {
121
+ "failure": failure,
122
+ "diagnosis": diagnosis,
123
+ "strategy": strategy,
124
+ "result": recovery_result,
125
+ "timestamp": datetime.now(timezone.utc).isoformat()
126
+ }
127
+
128
+ return recovery_result
129
+
130
+ def _determine_strategy(
131
+ self,
132
+ diagnosis: str,
133
+ manifest: CapabilityManifest,
134
+ has_compensation: bool
135
+ ) -> RecoveryStrategy:
136
+ """
137
+ Determine the appropriate recovery strategy.
138
+
139
+ Args:
140
+ diagnosis: Failure diagnosis from scak
141
+ manifest: Agent capability manifest
142
+ has_compensation: Whether compensation callback is available
143
+
144
+ Returns:
145
+ RecoveryStrategy to use for recovery
146
+ """
147
+ # Check if agent supports reversibility
148
+ reversibility = manifest.capabilities.reversibility.value
149
+
150
+ if reversibility in ["full", "partial"] and has_compensation:
151
+ # Agent supports rollback and we have compensation logic
152
+ return RecoveryStrategy.ROLLBACK
153
+ elif reversibility == "partial":
154
+ # Partial rollback - log and warn
155
+ return RecoveryStrategy.RETRY
156
+ elif diagnosis.lower().find("timeout") >= 0:
157
+ # Timeout errors might be transient
158
+ return RecoveryStrategy.RETRY
159
+ else:
160
+ # No recovery possible
161
+ return RecoveryStrategy.GIVE_UP
162
+
163
+ async def _execute_recovery(
164
+ self,
165
+ strategy: RecoveryStrategy,
166
+ trace_id: str,
167
+ failure: AgentFailure,
168
+ manifest: CapabilityManifest,
169
+ compensation_callback: Optional[Callable] = None
170
+ ) -> Dict[str, Any]:
171
+ """
172
+ Execute the determined recovery strategy.
173
+
174
+ Args:
175
+ strategy: Recovery strategy to execute
176
+ trace_id: Trace ID
177
+ failure: Failure details
178
+ manifest: Agent manifest
179
+ compensation_callback: Optional compensation callback
180
+
181
+ Returns:
182
+ Dictionary with recovery results
183
+ """
184
+ result = {
185
+ "strategy": strategy.value,
186
+ "success": False,
187
+ "actions_taken": [],
188
+ "trace_id": trace_id
189
+ }
190
+
191
+ if strategy == RecoveryStrategy.ROLLBACK:
192
+ logger.info(f"[Recovery] Executing rollback for {trace_id}")
193
+ result["actions_taken"].append("initiated_rollback")
194
+
195
+ if compensation_callback:
196
+ try:
197
+ # Execute compensation transaction
198
+ if asyncio.iscoroutinefunction(compensation_callback):
199
+ await compensation_callback()
200
+ else:
201
+ compensation_callback()
202
+
203
+ result["success"] = True
204
+ result["actions_taken"].append("compensation_executed")
205
+ logger.info(f"[Recovery] Rollback successful for {trace_id}")
206
+ except Exception as e:
207
+ result["actions_taken"].append(f"compensation_failed: {str(e)}")
208
+ logger.error(f"[Recovery] Rollback failed for {trace_id}: {e}")
209
+ else:
210
+ result["actions_taken"].append("no_compensation_available")
211
+ logger.warning(f"[Recovery] No compensation callback for {trace_id}")
212
+
213
+ elif strategy == RecoveryStrategy.RETRY:
214
+ logger.info(f"[Recovery] Retry recommended for {trace_id}")
215
+ result["actions_taken"].append("retry_recommended")
216
+ result["retry_possible"] = True
217
+ # Note: Actual retry logic would be implemented by the caller
218
+
219
+ else: # GIVE_UP
220
+ logger.info(f"[Recovery] No recovery possible for {trace_id}")
221
+ result["actions_taken"].append("recovery_not_possible")
222
+ result["message"] = (
223
+ f"Agent '{manifest.agent_id}' does not support rollback and "
224
+ "error is not recoverable. Transaction may be in inconsistent state."
225
+ )
226
+
227
+ return result
228
+
229
+ def get_recovery_history(self, trace_id: str) -> Optional[Dict[str, Any]]:
230
+ """
231
+ Get recovery history for a trace ID.
232
+
233
+ Args:
234
+ trace_id: Trace ID to look up
235
+
236
+ Returns:
237
+ Recovery history or None if not found
238
+ """
239
+ return self.recovery_history.get(trace_id)
240
+
241
+ def should_attempt_recovery(
242
+ self,
243
+ error: Exception,
244
+ manifest: CapabilityManifest
245
+ ) -> bool:
246
+ """
247
+ Determine if recovery should be attempted for this error.
248
+
249
+ Args:
250
+ error: The exception that occurred
251
+ manifest: Agent capability manifest
252
+
253
+ Returns:
254
+ True if recovery should be attempted
255
+ """
256
+ # Always attempt recovery if agent supports reversibility
257
+ if manifest.capabilities.reversibility.value in ["full", "partial"]:
258
+ return True
259
+
260
+ # Attempt recovery for certain error types
261
+ error_name = type(error).__name__
262
+ recoverable_errors = [
263
+ "TimeoutError",
264
+ "ConnectionError",
265
+ "HTTPError",
266
+ "ServiceUnavailable"
267
+ ]
268
+
269
+ return any(err in error_name for err in recoverable_errors)
270
+
271
+ async def execute_compensation_transaction(
272
+ self,
273
+ trace_id: str,
274
+ manifest: CapabilityManifest,
275
+ compensation_endpoint: str,
276
+ compensation_payload: Dict[str, Any]
277
+ ) -> bool:
278
+ """
279
+ Execute a compensation transaction using the agent's compensation endpoint.
280
+
281
+ This is used when the agent provides a specific compensation/rollback
282
+ endpoint as specified in the handshake.
283
+
284
+ Args:
285
+ trace_id: Trace ID
286
+ manifest: Agent manifest with undo_window info
287
+ compensation_endpoint: URL for compensation
288
+ compensation_payload: Payload for compensation request
289
+
290
+ Returns:
291
+ True if compensation succeeded
292
+ """
293
+ import httpx
294
+
295
+ logger.info(f"[Recovery] Executing compensation transaction for {trace_id}")
296
+
297
+ try:
298
+ async with httpx.AsyncClient() as client:
299
+ response = await client.post(
300
+ compensation_endpoint,
301
+ json=compensation_payload,
302
+ headers={
303
+ "X-Agent-Trace-ID": trace_id,
304
+ "X-Compensation-Request": "true"
305
+ },
306
+ timeout=30.0
307
+ )
308
+
309
+ if 200 <= response.status_code < 300:
310
+ logger.info(f"[Recovery] Compensation successful for {trace_id}")
311
+ return True
312
+ else:
313
+ logger.error(
314
+ f"[Recovery] Compensation failed for {trace_id}: "
315
+ f"status {response.status_code}"
316
+ )
317
+ return False
318
+
319
+ except Exception as e:
320
+ logger.error(f"[Recovery] Compensation exception for {trace_id}: {e}")
321
+ return False