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,281 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """
4
+ Tests for the IATP Recovery Engine (scak integration).
5
+ """
6
+ import pytest
7
+
8
+ from iatp.models import (
9
+ AgentCapabilities,
10
+ CapabilityManifest,
11
+ PrivacyContract,
12
+ RetentionPolicy,
13
+ ReversibilityLevel,
14
+ TrustLevel,
15
+ )
16
+ from iatp.recovery import IATPRecoveryEngine
17
+
18
+
19
+ @pytest.mark.asyncio
20
+ async def test_recovery_engine_initialization():
21
+ """Test that recovery engine initializes correctly."""
22
+ engine = IATPRecoveryEngine()
23
+ assert engine is not None
24
+ assert engine.recovery_history == {}
25
+
26
+
27
+ @pytest.mark.asyncio
28
+ async def test_handle_failure_with_reversibility():
29
+ """Test handling failure with reversible agent."""
30
+ engine = IATPRecoveryEngine()
31
+
32
+ manifest = CapabilityManifest(
33
+ agent_id="reversible-agent",
34
+ trust_level=TrustLevel.TRUSTED,
35
+ capabilities=AgentCapabilities(
36
+ idempotency=True,
37
+ reversibility=ReversibilityLevel.FULL,
38
+ ),
39
+ privacy_contract=PrivacyContract(
40
+ retention=RetentionPolicy.EPHEMERAL,
41
+ human_review=False,
42
+ )
43
+ )
44
+
45
+ error = Exception("Test error")
46
+ payload = {"test": "data"}
47
+
48
+ # Test without compensation callback
49
+ result = await engine.handle_failure(
50
+ trace_id="test-trace-1",
51
+ error=error,
52
+ manifest=manifest,
53
+ payload=payload,
54
+ compensation_callback=None
55
+ )
56
+
57
+ assert result is not None
58
+ assert "strategy" in result
59
+ assert "trace_id" in result
60
+ assert result["trace_id"] == "test-trace-1"
61
+
62
+
63
+ @pytest.mark.asyncio
64
+ async def test_handle_failure_with_compensation():
65
+ """Test handling failure with compensation callback."""
66
+ engine = IATPRecoveryEngine()
67
+
68
+ manifest = CapabilityManifest(
69
+ agent_id="compensating-agent",
70
+ trust_level=TrustLevel.TRUSTED,
71
+ capabilities=AgentCapabilities(
72
+ idempotency=True,
73
+ reversibility=ReversibilityLevel.FULL,
74
+ ),
75
+ privacy_contract=PrivacyContract(
76
+ retention=RetentionPolicy.EPHEMERAL,
77
+ human_review=False,
78
+ )
79
+ )
80
+
81
+ compensation_called = False
82
+
83
+ async def compensation_callback():
84
+ nonlocal compensation_called
85
+ compensation_called = True
86
+
87
+ error = Exception("Test error")
88
+ payload = {"test": "data"}
89
+
90
+ result = await engine.handle_failure(
91
+ trace_id="test-trace-2",
92
+ error=error,
93
+ manifest=manifest,
94
+ payload=payload,
95
+ compensation_callback=compensation_callback
96
+ )
97
+
98
+ assert result is not None
99
+ assert compensation_called is True
100
+ assert result["success"] is True
101
+ assert "compensation_executed" in result["actions_taken"]
102
+
103
+
104
+ @pytest.mark.asyncio
105
+ async def test_handle_failure_no_reversibility():
106
+ """Test handling failure with non-reversible agent."""
107
+ engine = IATPRecoveryEngine()
108
+
109
+ manifest = CapabilityManifest(
110
+ agent_id="non-reversible-agent",
111
+ trust_level=TrustLevel.STANDARD,
112
+ capabilities=AgentCapabilities(
113
+ idempotency=False,
114
+ reversibility=ReversibilityLevel.NONE,
115
+ ),
116
+ privacy_contract=PrivacyContract(
117
+ retention=RetentionPolicy.TEMPORARY,
118
+ human_review=False,
119
+ )
120
+ )
121
+
122
+ error = Exception("Test error")
123
+ payload = {"test": "data"}
124
+
125
+ result = await engine.handle_failure(
126
+ trace_id="test-trace-3",
127
+ error=error,
128
+ manifest=manifest,
129
+ payload=payload,
130
+ compensation_callback=None
131
+ )
132
+
133
+ assert result is not None
134
+ assert result["success"] is False
135
+ # Should give up or recommend retry
136
+ assert "strategy" in result
137
+
138
+
139
+ def test_should_attempt_recovery_reversible():
140
+ """Test recovery attempt decision for reversible agent."""
141
+ engine = IATPRecoveryEngine()
142
+
143
+ manifest = CapabilityManifest(
144
+ agent_id="reversible-agent",
145
+ trust_level=TrustLevel.TRUSTED,
146
+ capabilities=AgentCapabilities(
147
+ idempotency=True,
148
+ reversibility=ReversibilityLevel.FULL,
149
+ ),
150
+ privacy_contract=PrivacyContract(
151
+ retention=RetentionPolicy.EPHEMERAL,
152
+ human_review=False,
153
+ )
154
+ )
155
+
156
+ error = Exception("Test error")
157
+
158
+ should_recover = engine.should_attempt_recovery(error, manifest)
159
+
160
+ assert should_recover is True
161
+
162
+
163
+ def test_should_attempt_recovery_timeout():
164
+ """Test recovery attempt decision for timeout error."""
165
+ engine = IATPRecoveryEngine()
166
+
167
+ manifest = CapabilityManifest(
168
+ agent_id="non-reversible-agent",
169
+ trust_level=TrustLevel.STANDARD,
170
+ capabilities=AgentCapabilities(
171
+ idempotency=False,
172
+ reversibility=ReversibilityLevel.NONE,
173
+ ),
174
+ privacy_contract=PrivacyContract(
175
+ retention=RetentionPolicy.TEMPORARY,
176
+ human_review=False,
177
+ )
178
+ )
179
+
180
+ error = TimeoutError("Request timeout")
181
+
182
+ should_recover = engine.should_attempt_recovery(error, manifest)
183
+
184
+ # Should attempt recovery for timeout even without reversibility
185
+ assert should_recover is True
186
+
187
+
188
+ def test_get_recovery_history():
189
+ """Test retrieval of recovery history."""
190
+ engine = IATPRecoveryEngine()
191
+
192
+ # Add some history manually
193
+ engine.recovery_history["test-trace"] = {
194
+ "test": "data"
195
+ }
196
+
197
+ history = engine.get_recovery_history("test-trace")
198
+
199
+ assert history is not None
200
+ assert history["test"] == "data"
201
+
202
+ # Non-existent trace
203
+ missing_history = engine.get_recovery_history("non-existent")
204
+ assert missing_history is None
205
+
206
+
207
+ @pytest.mark.asyncio
208
+ async def test_handle_failure_compensation_exception():
209
+ """Test handling failure when compensation callback raises exception."""
210
+ engine = IATPRecoveryEngine()
211
+
212
+ manifest = CapabilityManifest(
213
+ agent_id="failing-compensation-agent",
214
+ trust_level=TrustLevel.TRUSTED,
215
+ capabilities=AgentCapabilities(
216
+ idempotency=True,
217
+ reversibility=ReversibilityLevel.FULL,
218
+ ),
219
+ privacy_contract=PrivacyContract(
220
+ retention=RetentionPolicy.EPHEMERAL,
221
+ human_review=False,
222
+ )
223
+ )
224
+
225
+ async def failing_compensation():
226
+ raise Exception("Compensation failed")
227
+
228
+ error = Exception("Test error")
229
+ payload = {"test": "data"}
230
+
231
+ result = await engine.handle_failure(
232
+ trace_id="test-trace-4",
233
+ error=error,
234
+ manifest=manifest,
235
+ payload=payload,
236
+ compensation_callback=failing_compensation
237
+ )
238
+
239
+ assert result is not None
240
+ assert result["success"] is False
241
+ assert any("compensation_failed" in action for action in result["actions_taken"])
242
+
243
+
244
+ @pytest.mark.asyncio
245
+ async def test_handle_failure_sync_callback():
246
+ """Test handling failure with synchronous compensation callback."""
247
+ engine = IATPRecoveryEngine()
248
+
249
+ manifest = CapabilityManifest(
250
+ agent_id="sync-compensation-agent",
251
+ trust_level=TrustLevel.TRUSTED,
252
+ capabilities=AgentCapabilities(
253
+ idempotency=True,
254
+ reversibility=ReversibilityLevel.FULL,
255
+ ),
256
+ privacy_contract=PrivacyContract(
257
+ retention=RetentionPolicy.EPHEMERAL,
258
+ human_review=False,
259
+ )
260
+ )
261
+
262
+ compensation_called = False
263
+
264
+ def sync_compensation():
265
+ nonlocal compensation_called
266
+ compensation_called = True
267
+
268
+ error = Exception("Test error")
269
+ payload = {"test": "data"}
270
+
271
+ result = await engine.handle_failure(
272
+ trace_id="test-trace-5",
273
+ error=error,
274
+ manifest=manifest,
275
+ payload=payload,
276
+ compensation_callback=sync_compensation
277
+ )
278
+
279
+ assert result is not None
280
+ assert compensation_called is True
281
+ assert result["success"] is True
@@ -0,0 +1,222 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """
4
+ Unit tests for IATP security module.
5
+ """
6
+ from iatp.models import (
7
+ AgentCapabilities,
8
+ CapabilityManifest,
9
+ PrivacyContract,
10
+ RetentionPolicy,
11
+ ReversibilityLevel,
12
+ TrustLevel,
13
+ )
14
+ from iatp.security import PrivacyScrubber, SecurityValidator
15
+
16
+
17
+ def test_detect_credit_card():
18
+ """Test credit card detection with Luhn validation."""
19
+ validator = SecurityValidator()
20
+
21
+ # Test various credit card formats (using valid test card numbers)
22
+ # 4532015112830366 is a valid test Visa card that passes Luhn check
23
+ payload1 = {"data": "My card is 4532-0151-1283-0366"}
24
+ sensitive1 = validator.detect_sensitive_data(payload1)
25
+ assert "credit_card" in sensitive1
26
+
27
+ payload2 = {"data": "My card is 4532 0151 1283 0366"}
28
+ sensitive2 = validator.detect_sensitive_data(payload2)
29
+ assert "credit_card" in sensitive2
30
+
31
+ payload3 = {"data": "My card is 4532015112830366"}
32
+ sensitive3 = validator.detect_sensitive_data(payload3)
33
+ assert "credit_card" in sensitive3
34
+
35
+ # Test with invalid card number (should not be detected)
36
+ payload4 = {"data": "My card is 1234-5678-9012-3456"}
37
+ sensitive4 = validator.detect_sensitive_data(payload4)
38
+ assert "credit_card" not in sensitive4
39
+
40
+
41
+ def test_detect_ssn():
42
+ """Test SSN detection."""
43
+ validator = SecurityValidator()
44
+
45
+ payload = {"data": "My SSN is 123-45-6789"}
46
+ sensitive = validator.detect_sensitive_data(payload)
47
+ assert "ssn" in sensitive
48
+
49
+
50
+ def test_detect_email():
51
+ """Test email detection."""
52
+ validator = SecurityValidator()
53
+
54
+ payload = {"data": "Contact me at test@example.com"}
55
+ sensitive = validator.detect_sensitive_data(payload)
56
+ assert "email" in sensitive
57
+
58
+
59
+ def test_validate_privacy_policy_blocks_credit_card_forever():
60
+ """Test that credit cards are blocked for permanent retention."""
61
+ validator = SecurityValidator()
62
+ manifest = CapabilityManifest(
63
+ agent_id="bad-agent",
64
+ trust_level=TrustLevel.UNTRUSTED,
65
+ capabilities=AgentCapabilities(),
66
+ privacy_contract=PrivacyContract(
67
+ retention=RetentionPolicy.FOREVER
68
+ )
69
+ )
70
+
71
+ payload = {"credit_card": "4532-0151-1283-0366"} # Valid test card
72
+ is_valid, error = validator.validate_privacy_policy(manifest, payload)
73
+
74
+ assert not is_valid
75
+ assert "Privacy Violation" in error
76
+ assert "credit card" in error.lower()
77
+
78
+
79
+ def test_validate_privacy_policy_allows_credit_card_ephemeral():
80
+ """Test that credit cards are allowed for ephemeral retention."""
81
+ validator = SecurityValidator()
82
+ manifest = CapabilityManifest(
83
+ agent_id="good-agent",
84
+ trust_level=TrustLevel.VERIFIED_PARTNER,
85
+ capabilities=AgentCapabilities(),
86
+ privacy_contract=PrivacyContract(
87
+ retention=RetentionPolicy.EPHEMERAL
88
+ )
89
+ )
90
+
91
+ payload = {"credit_card": "4532-0151-1283-0366"} # Valid test card
92
+ is_valid, error = validator.validate_privacy_policy(manifest, payload)
93
+
94
+ assert is_valid
95
+ assert error is None
96
+
97
+
98
+ def test_validate_privacy_policy_blocks_ssn_non_ephemeral():
99
+ """Test that SSN is blocked for non-ephemeral retention."""
100
+ validator = SecurityValidator()
101
+ manifest = CapabilityManifest(
102
+ agent_id="medium-agent",
103
+ trust_level=TrustLevel.STANDARD,
104
+ capabilities=AgentCapabilities(),
105
+ privacy_contract=PrivacyContract(
106
+ retention=RetentionPolicy.TEMPORARY
107
+ )
108
+ )
109
+
110
+ payload = {"ssn": "123-45-6789"}
111
+ is_valid, error = validator.validate_privacy_policy(manifest, payload)
112
+
113
+ assert not is_valid
114
+ assert "SSN" in error
115
+
116
+
117
+ def test_generate_warning_low_trust():
118
+ """Test warning generation for low trust agents."""
119
+ validator = SecurityValidator()
120
+ manifest = CapabilityManifest(
121
+ agent_id="sketchy-agent",
122
+ trust_level=TrustLevel.UNTRUSTED,
123
+ capabilities=AgentCapabilities(
124
+ idempotency=False,
125
+ reversibility=ReversibilityLevel.NONE
126
+ ),
127
+ privacy_contract=PrivacyContract(
128
+ retention=RetentionPolicy.FOREVER,
129
+ human_review=True
130
+ )
131
+ )
132
+
133
+ warning = validator.generate_warning_message(manifest, {})
134
+
135
+ assert warning is not None
136
+ assert "Low trust score" in warning
137
+ assert "does not support transaction reversal" in warning
138
+ assert "stores data indefinitely" in warning
139
+ assert "may have humans review your data" in warning
140
+
141
+
142
+ def test_generate_warning_trusted_agent():
143
+ """Test that no warning is generated for trusted agents."""
144
+ validator = SecurityValidator()
145
+ manifest = CapabilityManifest(
146
+ agent_id="trusted-agent",
147
+ trust_level=TrustLevel.VERIFIED_PARTNER,
148
+ capabilities=AgentCapabilities(
149
+ idempotency=True,
150
+ reversibility=ReversibilityLevel.FULL
151
+ ),
152
+ privacy_contract=PrivacyContract(
153
+ retention=RetentionPolicy.EPHEMERAL,
154
+ human_review=False
155
+ )
156
+ )
157
+
158
+ warning = validator.generate_warning_message(manifest, {})
159
+ assert warning is None
160
+
161
+
162
+ def test_should_quarantine_untrusted():
163
+ """Test quarantine decision for untrusted agents."""
164
+ validator = SecurityValidator()
165
+ manifest = CapabilityManifest(
166
+ agent_id="untrusted-agent",
167
+ trust_level=TrustLevel.UNTRUSTED,
168
+ capabilities=AgentCapabilities(),
169
+ privacy_contract=PrivacyContract(
170
+ retention=RetentionPolicy.EPHEMERAL
171
+ )
172
+ )
173
+
174
+ assert validator.should_quarantine(manifest)
175
+
176
+
177
+ def test_should_quarantine_no_reversibility_permanent():
178
+ """Test quarantine for no reversibility and permanent storage."""
179
+ validator = SecurityValidator()
180
+ manifest = CapabilityManifest(
181
+ agent_id="risky-agent",
182
+ trust_level=TrustLevel.STANDARD,
183
+ capabilities=AgentCapabilities(
184
+ reversibility=ReversibilityLevel.NONE
185
+ ),
186
+ privacy_contract=PrivacyContract(
187
+ retention=RetentionPolicy.FOREVER
188
+ )
189
+ )
190
+
191
+ assert validator.should_quarantine(manifest)
192
+
193
+
194
+ def test_scrub_credit_card():
195
+ """Test scrubbing credit card from payload."""
196
+ payload = {
197
+ "user": "john",
198
+ "payment": {
199
+ "card": "4532-0151-1283-0366", # Valid test card
200
+ "cvv": "123"
201
+ }
202
+ }
203
+
204
+ scrubbed = PrivacyScrubber.scrub_payload(payload)
205
+
206
+ scrubbed_str = str(scrubbed)
207
+ assert "4532-0151-1283-0366" not in scrubbed_str
208
+ assert "[CREDIT_CARD_REDACTED]" in scrubbed_str
209
+
210
+
211
+ def test_scrub_ssn():
212
+ """Test scrubbing SSN from payload."""
213
+ payload = {
214
+ "user": "john",
215
+ "ssn": "123-45-6789"
216
+ }
217
+
218
+ scrubbed = PrivacyScrubber.scrub_payload(payload)
219
+
220
+ scrubbed_str = str(scrubbed)
221
+ assert "123-45-6789" not in scrubbed_str
222
+ assert "[SSN_REDACTED]" in scrubbed_str
@@ -0,0 +1,167 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """
4
+ Integration tests for the IATP Sidecar.
5
+ """
6
+ import pytest
7
+ from fastapi.testclient import TestClient
8
+
9
+ from iatp.models import (
10
+ AgentCapabilities,
11
+ CapabilityManifest,
12
+ PrivacyContract,
13
+ RetentionPolicy,
14
+ ReversibilityLevel,
15
+ TrustLevel,
16
+ )
17
+ from iatp.sidecar import create_sidecar
18
+
19
+
20
+ @pytest.fixture
21
+ def trusted_manifest():
22
+ """Create a trusted agent manifest for testing."""
23
+ return CapabilityManifest(
24
+ agent_id="test-trusted-agent",
25
+ agent_version="1.0.0",
26
+ trust_level=TrustLevel.VERIFIED_PARTNER,
27
+ capabilities=AgentCapabilities(
28
+ idempotency=True,
29
+ reversibility=ReversibilityLevel.FULL,
30
+ undo_window="24h",
31
+ sla_latency="1000ms"
32
+ ),
33
+ privacy_contract=PrivacyContract(
34
+ retention=RetentionPolicy.EPHEMERAL,
35
+ storage_location="us-east",
36
+ human_review=False
37
+ )
38
+ )
39
+
40
+
41
+ @pytest.fixture
42
+ def untrusted_manifest():
43
+ """Create an untrusted agent manifest for testing."""
44
+ return CapabilityManifest(
45
+ agent_id="test-untrusted-agent",
46
+ agent_version="0.1.0",
47
+ trust_level=TrustLevel.UNTRUSTED,
48
+ capabilities=AgentCapabilities(
49
+ idempotency=False,
50
+ reversibility=ReversibilityLevel.NONE
51
+ ),
52
+ privacy_contract=PrivacyContract(
53
+ retention=RetentionPolicy.FOREVER,
54
+ storage_location="unknown",
55
+ human_review=True
56
+ )
57
+ )
58
+
59
+
60
+ def test_health_check(trusted_manifest):
61
+ """Test the health check endpoint."""
62
+ sidecar = create_sidecar(
63
+ agent_url="http://localhost:9999",
64
+ manifest=trusted_manifest
65
+ )
66
+ client = TestClient(sidecar.app)
67
+
68
+ response = client.get("/health")
69
+ assert response.status_code == 200
70
+ data = response.json()
71
+ assert data["status"] == "healthy"
72
+ assert data["agent_id"] == "test-trusted-agent"
73
+
74
+
75
+ def test_get_manifest(trusted_manifest):
76
+ """Test getting the capability manifest."""
77
+ sidecar = create_sidecar(
78
+ agent_url="http://localhost:9999",
79
+ manifest=trusted_manifest
80
+ )
81
+ client = TestClient(sidecar.app)
82
+
83
+ response = client.get("/.well-known/agent-manifest")
84
+ assert response.status_code == 200
85
+ data = response.json()
86
+ assert data["agent_id"] == "test-trusted-agent"
87
+ assert data["trust_level"] == "verified_partner"
88
+ assert data["capabilities"]["idempotency"] is True
89
+
90
+
91
+ def test_invalid_json_payload(trusted_manifest):
92
+ """Test that invalid JSON is rejected."""
93
+ sidecar = create_sidecar(
94
+ agent_url="http://localhost:9999",
95
+ manifest=trusted_manifest
96
+ )
97
+ client = TestClient(sidecar.app)
98
+
99
+ response = client.post(
100
+ "/proxy",
101
+ content="invalid json",
102
+ headers={"Content-Type": "application/json"}
103
+ )
104
+ assert response.status_code == 400
105
+ data = response.json()
106
+ assert "Invalid JSON" in data["error"]
107
+
108
+
109
+ def test_blocked_credit_card_permanent_storage(untrusted_manifest):
110
+ """Test that credit cards are blocked for permanent storage."""
111
+ sidecar = create_sidecar(
112
+ agent_url="http://localhost:9999",
113
+ manifest=untrusted_manifest
114
+ )
115
+ client = TestClient(sidecar.app)
116
+
117
+ response = client.post(
118
+ "/proxy",
119
+ json={
120
+ "task": "purchase",
121
+ "card": "4532-0151-1283-0366" # Valid test card
122
+ }
123
+ )
124
+ assert response.status_code == 403
125
+ data = response.json()
126
+ assert "Privacy Violation" in data["error"]
127
+ assert data["blocked"] is True
128
+
129
+
130
+ def test_warning_without_override(untrusted_manifest):
131
+ """Test that untrusted agents trigger warnings."""
132
+ sidecar = create_sidecar(
133
+ agent_url="http://localhost:9999",
134
+ manifest=untrusted_manifest
135
+ )
136
+ client = TestClient(sidecar.app)
137
+
138
+ response = client.post(
139
+ "/proxy",
140
+ json={"task": "test", "data": {}}
141
+ )
142
+ assert response.status_code == 449
143
+ data = response.json()
144
+ assert "warning" in data
145
+ assert "requires_override" in data
146
+ assert data["requires_override"] is True
147
+
148
+
149
+ def test_trace_id_injection(trusted_manifest):
150
+ """Test that trace IDs are properly handled."""
151
+ sidecar = create_sidecar(
152
+ agent_url="http://localhost:9999",
153
+ manifest=trusted_manifest
154
+ )
155
+ client = TestClient(sidecar.app)
156
+
157
+ # Provide a custom trace ID
158
+ custom_trace_id = "custom-trace-123"
159
+ response = client.post(
160
+ "/proxy",
161
+ json={"task": "test"},
162
+ headers={"X-Agent-Trace-ID": custom_trace_id}
163
+ )
164
+
165
+ # Even if backend fails, trace ID should be in error response
166
+ data = response.json()
167
+ assert "trace_id" in data