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,211 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """Tests for Ed25519 cryptographic attestation in IATP."""
4
+
5
+ import pytest
6
+ from iatp.attestation import (
7
+ AttestationValidator,
8
+ generate_ed25519_keypair,
9
+ )
10
+
11
+
12
+ @pytest.fixture
13
+ def keypair():
14
+ """Generate a fresh Ed25519 key pair."""
15
+ return generate_ed25519_keypair()
16
+
17
+
18
+ @pytest.fixture
19
+ def validator_with_key(keypair):
20
+ """Create an AttestationValidator with a registered public key."""
21
+ _, public_b64 = keypair
22
+ v = AttestationValidator()
23
+ v.add_trusted_key("test-key", public_b64)
24
+ return v
25
+
26
+
27
+ class TestGenerateKeypair:
28
+ def test_returns_two_strings(self):
29
+ priv, pub = generate_ed25519_keypair()
30
+ assert isinstance(priv, str)
31
+ assert isinstance(pub, str)
32
+
33
+ def test_keys_are_base64(self):
34
+ import base64
35
+ priv, pub = generate_ed25519_keypair()
36
+ priv_bytes = base64.b64decode(priv)
37
+ pub_bytes = base64.b64decode(pub)
38
+ assert len(priv_bytes) == 32 # Ed25519 private key is 32 bytes
39
+ assert len(pub_bytes) == 32 # Ed25519 public key is 32 bytes
40
+
41
+ def test_unique_each_call(self):
42
+ _, pub1 = generate_ed25519_keypair()
43
+ _, pub2 = generate_ed25519_keypair()
44
+ assert pub1 != pub2
45
+
46
+
47
+ class TestRealCryptoSignAndVerify:
48
+ def test_sign_and_verify_roundtrip(self, keypair, validator_with_key):
49
+ """Create an attestation with real key, verify with real crypto."""
50
+ priv_b64, _ = keypair
51
+ v = validator_with_key
52
+
53
+ attestation = v.create_attestation(
54
+ agent_id="agent-001",
55
+ codebase_hash="aabbccdd",
56
+ config_hash="11223344",
57
+ signing_key_id="test-key",
58
+ private_key=priv_b64,
59
+ )
60
+
61
+ is_valid, error = v.validate_attestation(attestation, verify_signature=True)
62
+ assert is_valid, f"Verification failed: {error}"
63
+ assert error is None
64
+
65
+ def test_tampered_signature_rejected(self, keypair, validator_with_key):
66
+ """Tampered signature must be rejected."""
67
+ import base64
68
+ priv_b64, _ = keypair
69
+ v = validator_with_key
70
+
71
+ attestation = v.create_attestation(
72
+ agent_id="agent-001",
73
+ codebase_hash="aabbccdd",
74
+ config_hash="11223344",
75
+ signing_key_id="test-key",
76
+ private_key=priv_b64,
77
+ )
78
+
79
+ # Tamper with the signature (flip a byte)
80
+ sig_bytes = bytearray(base64.b64decode(attestation.signature))
81
+ sig_bytes[0] ^= 0xFF
82
+ attestation.signature = base64.b64encode(bytes(sig_bytes)).decode()
83
+
84
+ is_valid, error = v.validate_attestation(attestation, verify_signature=True)
85
+ assert not is_valid
86
+ assert "invalid signature" in error.lower()
87
+
88
+ def test_tampered_agent_id_rejected(self, keypair, validator_with_key):
89
+ """Changing the agent_id after signing must be rejected."""
90
+ priv_b64, _ = keypair
91
+ v = validator_with_key
92
+
93
+ attestation = v.create_attestation(
94
+ agent_id="agent-001",
95
+ codebase_hash="aabbccdd",
96
+ config_hash="11223344",
97
+ signing_key_id="test-key",
98
+ private_key=priv_b64,
99
+ )
100
+
101
+ # Tamper with agent_id (message changes, signature invalid)
102
+ attestation.agent_id = "evil-agent"
103
+
104
+ is_valid, error = v.validate_attestation(attestation, verify_signature=True)
105
+ assert not is_valid
106
+ assert "invalid signature" in error.lower()
107
+
108
+ def test_tampered_codebase_hash_rejected(self, keypair, validator_with_key):
109
+ """Changing the codebase_hash after signing must be rejected."""
110
+ priv_b64, _ = keypair
111
+ v = validator_with_key
112
+
113
+ attestation = v.create_attestation(
114
+ agent_id="agent-001",
115
+ codebase_hash="aabbccdd",
116
+ config_hash="11223344",
117
+ signing_key_id="test-key",
118
+ private_key=priv_b64,
119
+ )
120
+
121
+ attestation.codebase_hash = "deadbeef"
122
+
123
+ is_valid, error = v.validate_attestation(attestation, verify_signature=True)
124
+ assert not is_valid
125
+
126
+ def test_wrong_key_rejected(self, keypair):
127
+ """Signature made with one key must fail with a different public key."""
128
+ priv_b64, _ = keypair
129
+ _, other_pub = generate_ed25519_keypair()
130
+
131
+ v = AttestationValidator()
132
+ v.add_trusted_key("other-key", other_pub)
133
+
134
+ # Sign with original key, register different public key
135
+ attestation = v.create_attestation(
136
+ agent_id="agent-001",
137
+ codebase_hash="aabb",
138
+ config_hash="ccdd",
139
+ signing_key_id="other-key",
140
+ private_key=priv_b64,
141
+ )
142
+
143
+ is_valid, error = v.validate_attestation(attestation, verify_signature=True)
144
+ assert not is_valid
145
+ assert "invalid signature" in error.lower()
146
+
147
+ def test_unsigned_attestation_accepted_without_verify_flag(self, validator_with_key):
148
+ """When verify_signature=False, unsigned attestations pass."""
149
+ v = validator_with_key
150
+ attestation = v.create_attestation(
151
+ agent_id="agent-001",
152
+ codebase_hash="aabb",
153
+ config_hash="ccdd",
154
+ signing_key_id="test-key",
155
+ private_key=None, # No signing
156
+ )
157
+
158
+ is_valid, error = v.validate_attestation(attestation, verify_signature=False)
159
+ assert is_valid
160
+
161
+ def test_unsigned_attestation_rejected_with_verify_flag(self, validator_with_key):
162
+ """Unsigned attestation fails when verify_signature=True."""
163
+ v = validator_with_key
164
+ attestation = v.create_attestation(
165
+ agent_id="agent-001",
166
+ codebase_hash="aabb",
167
+ config_hash="ccdd",
168
+ signing_key_id="test-key",
169
+ private_key=None, # No real signing — base64 of message, not Ed25519
170
+ )
171
+
172
+ is_valid, error = v.validate_attestation(attestation, verify_signature=True)
173
+ assert not is_valid
174
+
175
+ def test_multiple_agents_different_keys(self):
176
+ """Each agent can have its own key pair."""
177
+ v = AttestationValidator()
178
+
179
+ priv1, pub1 = generate_ed25519_keypair()
180
+ priv2, pub2 = generate_ed25519_keypair()
181
+ v.add_trusted_key("key-agent1", pub1)
182
+ v.add_trusted_key("key-agent2", pub2)
183
+
184
+ att1 = v.create_attestation("agent-1", "h1", "c1", "key-agent1", priv1)
185
+ att2 = v.create_attestation("agent-2", "h2", "c2", "key-agent2", priv2)
186
+
187
+ assert v.validate_attestation(att1, verify_signature=True) == (True, None)
188
+ assert v.validate_attestation(att2, verify_signature=True) == (True, None)
189
+
190
+ # Cross-verify should fail (agent-1's attestation with agent-2's key)
191
+ att1.signing_key_id = "key-agent2"
192
+ is_valid, _ = v.validate_attestation(att1, verify_signature=True)
193
+ assert not is_valid
194
+
195
+ def test_expired_attestation_rejected_even_with_valid_sig(self, keypair, validator_with_key):
196
+ """Valid signature doesn't help if attestation is expired."""
197
+ priv_b64, _ = keypair
198
+ v = validator_with_key
199
+
200
+ attestation = v.create_attestation(
201
+ agent_id="agent-001",
202
+ codebase_hash="aabb",
203
+ config_hash="ccdd",
204
+ signing_key_id="test-key",
205
+ private_key=priv_b64,
206
+ expires_in_hours=-1, # Already expired
207
+ )
208
+
209
+ is_valid, error = v.validate_attestation(attestation, verify_signature=True)
210
+ assert not is_valid
211
+ assert "expired" in error.lower()
@@ -0,0 +1,130 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """
4
+ Unit tests for IATP models.
5
+ """
6
+ from iatp.models import (
7
+ AgentCapabilities,
8
+ CapabilityManifest,
9
+ PrivacyContract,
10
+ RetentionPolicy,
11
+ ReversibilityLevel,
12
+ TrustLevel,
13
+ )
14
+
15
+
16
+ def test_capability_manifest_creation():
17
+ """Test creating a capability manifest."""
18
+ manifest = CapabilityManifest(
19
+ agent_id="test-agent",
20
+ agent_version="1.0.0",
21
+ trust_level=TrustLevel.TRUSTED,
22
+ capabilities=AgentCapabilities(
23
+ idempotency=True,
24
+ reversibility=ReversibilityLevel.FULL,
25
+ undo_window="24h",
26
+ sla_latency="1000ms"
27
+ ),
28
+ privacy_contract=PrivacyContract(
29
+ retention=RetentionPolicy.EPHEMERAL,
30
+ storage_location="us-east",
31
+ human_review=False
32
+ )
33
+ )
34
+
35
+ assert manifest.agent_id == "test-agent"
36
+ assert manifest.trust_level == TrustLevel.TRUSTED
37
+ assert manifest.capabilities.idempotency is True
38
+ assert manifest.privacy_contract.retention == RetentionPolicy.EPHEMERAL
39
+
40
+
41
+ def test_capability_manifest_with_scopes():
42
+ """Test creating a capability manifest with RBAC scopes."""
43
+ manifest = CapabilityManifest(
44
+ agent_id="coder-agent",
45
+ agent_version="1.0.0",
46
+ trust_level=TrustLevel.TRUSTED,
47
+ capabilities=AgentCapabilities(
48
+ idempotency=True,
49
+ reversibility=ReversibilityLevel.FULL,
50
+ ),
51
+ privacy_contract=PrivacyContract(
52
+ retention=RetentionPolicy.EPHEMERAL,
53
+ ),
54
+ scopes=["repo:read", "repo:write"]
55
+ )
56
+
57
+ assert manifest.agent_id == "coder-agent"
58
+ assert manifest.scopes == ["repo:read", "repo:write"]
59
+
60
+
61
+ def test_capability_manifest_default_scopes():
62
+ """Test that scopes defaults to empty list."""
63
+ manifest = CapabilityManifest(
64
+ agent_id="agent-without-scopes",
65
+ trust_level=TrustLevel.STANDARD,
66
+ capabilities=AgentCapabilities(),
67
+ privacy_contract=PrivacyContract(
68
+ retention=RetentionPolicy.TEMPORARY,
69
+ )
70
+ )
71
+
72
+ assert manifest.scopes == []
73
+
74
+
75
+ def test_trust_score_verified_partner():
76
+ """Test trust score for verified partner."""
77
+ manifest = CapabilityManifest(
78
+ agent_id="verified-agent",
79
+ trust_level=TrustLevel.VERIFIED_PARTNER,
80
+ capabilities=AgentCapabilities(
81
+ idempotency=True,
82
+ reversibility=ReversibilityLevel.FULL
83
+ ),
84
+ privacy_contract=PrivacyContract(
85
+ retention=RetentionPolicy.EPHEMERAL,
86
+ human_review=False
87
+ )
88
+ )
89
+
90
+ score = manifest.calculate_trust_score()
91
+ assert score >= 8 # Verified partner with good privacy should score high
92
+
93
+
94
+ def test_trust_score_untrusted():
95
+ """Test trust score for untrusted agent."""
96
+ manifest = CapabilityManifest(
97
+ agent_id="sketchy-agent",
98
+ trust_level=TrustLevel.UNTRUSTED,
99
+ capabilities=AgentCapabilities(
100
+ idempotency=False,
101
+ reversibility=ReversibilityLevel.NONE
102
+ ),
103
+ privacy_contract=PrivacyContract(
104
+ retention=RetentionPolicy.FOREVER,
105
+ human_review=True
106
+ )
107
+ )
108
+
109
+ score = manifest.calculate_trust_score()
110
+ assert score <= 3 # Untrusted agent with bad privacy should score low
111
+
112
+
113
+ def test_privacy_contract_defaults():
114
+ """Test privacy contract default values."""
115
+ contract = PrivacyContract(
116
+ retention=RetentionPolicy.TEMPORARY
117
+ )
118
+
119
+ assert contract.human_review is False
120
+ assert contract.encryption_at_rest is True
121
+ assert contract.encryption_in_transit is True
122
+
123
+
124
+ def test_agent_capabilities_defaults():
125
+ """Test agent capabilities default values."""
126
+ capabilities = AgentCapabilities()
127
+
128
+ assert capabilities.idempotency is False
129
+ assert capabilities.reversibility == ReversibilityLevel.NONE
130
+ assert capabilities.undo_window is None
@@ -0,0 +1,347 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """
4
+ Tests for the IATP Policy Engine.
5
+ """
6
+ from iatp.models import (
7
+ AgentCapabilities,
8
+ CapabilityManifest,
9
+ PrivacyContract,
10
+ RetentionPolicy,
11
+ ReversibilityLevel,
12
+ TrustLevel,
13
+ )
14
+ from iatp.policy_engine import IATPPolicyEngine
15
+
16
+
17
+ def test_policy_engine_initialization():
18
+ """Test that policy engine initializes with default policies."""
19
+ engine = IATPPolicyEngine()
20
+ assert engine is not None
21
+ assert len(engine.rules) > 0
22
+
23
+
24
+ def test_validate_manifest_ephemeral_allowed():
25
+ """Test that ephemeral retention is allowed."""
26
+ engine = IATPPolicyEngine()
27
+
28
+ manifest = CapabilityManifest(
29
+ agent_id="test-agent",
30
+ trust_level=TrustLevel.TRUSTED,
31
+ capabilities=AgentCapabilities(
32
+ idempotency=True,
33
+ reversibility=ReversibilityLevel.FULL,
34
+ ),
35
+ privacy_contract=PrivacyContract(
36
+ retention=RetentionPolicy.EPHEMERAL,
37
+ human_review=False,
38
+ )
39
+ )
40
+
41
+ is_allowed, error_msg, warning_msg = engine.validate_manifest(manifest)
42
+
43
+ assert is_allowed is True
44
+ assert error_msg is None
45
+ # May have warning for other reasons, but should not block
46
+
47
+
48
+ def test_validate_manifest_permanent_warning():
49
+ """Test that permanent retention generates a warning but doesn't block."""
50
+ engine = IATPPolicyEngine()
51
+
52
+ manifest = CapabilityManifest(
53
+ agent_id="permanent-storage-agent",
54
+ trust_level=TrustLevel.STANDARD,
55
+ capabilities=AgentCapabilities(
56
+ idempotency=False,
57
+ reversibility=ReversibilityLevel.NONE,
58
+ ),
59
+ privacy_contract=PrivacyContract(
60
+ retention=RetentionPolicy.PERMANENT,
61
+ human_review=True,
62
+ )
63
+ )
64
+
65
+ is_allowed, error_msg, warning_msg = engine.validate_manifest(manifest)
66
+
67
+ # Should be allowed but may have warnings
68
+ # (Actual blocking based on sensitive data happens in SecurityValidator)
69
+ assert is_allowed is True
70
+ # May have warning about no reversibility
71
+ if warning_msg:
72
+ assert "reversal" in warning_msg.lower() or "warning" in warning_msg.lower()
73
+
74
+
75
+ def test_validate_manifest_no_reversibility_warning():
76
+ """Test that no reversibility generates warning."""
77
+ engine = IATPPolicyEngine()
78
+
79
+ manifest = CapabilityManifest(
80
+ agent_id="limited-agent",
81
+ trust_level=TrustLevel.STANDARD,
82
+ capabilities=AgentCapabilities(
83
+ idempotency=True,
84
+ reversibility=ReversibilityLevel.NONE,
85
+ ),
86
+ privacy_contract=PrivacyContract(
87
+ retention=RetentionPolicy.TEMPORARY,
88
+ human_review=False,
89
+ )
90
+ )
91
+
92
+ is_allowed, error_msg, warning_msg = engine.validate_manifest(manifest)
93
+
94
+ assert is_allowed is True
95
+ assert error_msg is None
96
+ # Warning about no reversibility
97
+ if warning_msg:
98
+ assert "reversal" in warning_msg.lower() or "warning" in warning_msg.lower()
99
+
100
+
101
+ def test_add_custom_rule():
102
+ """Test adding custom policy rules."""
103
+ engine = IATPPolicyEngine()
104
+
105
+ custom_rule = {
106
+ "name": "CustomTestRule",
107
+ "description": "Test custom rule",
108
+ "action": "deny",
109
+ "conditions": {"trust_level": ["untrusted"]}
110
+ }
111
+
112
+ engine.add_custom_rule(custom_rule)
113
+
114
+ # Test with untrusted agent
115
+ manifest = CapabilityManifest(
116
+ agent_id="untrusted-agent",
117
+ trust_level=TrustLevel.UNTRUSTED,
118
+ capabilities=AgentCapabilities(
119
+ idempotency=True,
120
+ reversibility=ReversibilityLevel.FULL,
121
+ ),
122
+ privacy_contract=PrivacyContract(
123
+ retention=RetentionPolicy.EPHEMERAL,
124
+ human_review=False,
125
+ )
126
+ )
127
+
128
+ is_allowed, error_msg, warning_msg = engine.validate_manifest(manifest)
129
+
130
+ # Should be blocked by custom rule or existing rules
131
+ assert is_allowed is False or warning_msg is not None
132
+
133
+
134
+ def test_validate_handshake_compatible():
135
+ """Test handshake validation with compatible agent."""
136
+ engine = IATPPolicyEngine()
137
+
138
+ manifest = CapabilityManifest(
139
+ agent_id="compatible-agent",
140
+ trust_level=TrustLevel.TRUSTED,
141
+ capabilities=AgentCapabilities(
142
+ idempotency=True,
143
+ reversibility=ReversibilityLevel.FULL,
144
+ ),
145
+ privacy_contract=PrivacyContract(
146
+ retention=RetentionPolicy.EPHEMERAL,
147
+ human_review=False,
148
+ )
149
+ )
150
+
151
+ is_compatible, error_msg = engine.validate_handshake(
152
+ manifest,
153
+ required_capabilities=["reversibility", "idempotency"]
154
+ )
155
+
156
+ assert is_compatible is True
157
+ assert error_msg is None
158
+
159
+
160
+ def test_validate_handshake_missing_capabilities():
161
+ """Test handshake validation with missing capabilities."""
162
+ engine = IATPPolicyEngine()
163
+
164
+ manifest = CapabilityManifest(
165
+ agent_id="limited-agent",
166
+ trust_level=TrustLevel.STANDARD,
167
+ capabilities=AgentCapabilities(
168
+ idempotency=False,
169
+ reversibility=ReversibilityLevel.NONE,
170
+ ),
171
+ privacy_contract=PrivacyContract(
172
+ retention=RetentionPolicy.TEMPORARY,
173
+ human_review=False,
174
+ )
175
+ )
176
+
177
+ is_compatible, error_msg = engine.validate_handshake(
178
+ manifest,
179
+ required_capabilities=["reversibility"]
180
+ )
181
+
182
+ assert is_compatible is False
183
+ assert error_msg is not None
184
+ assert "reversibility" in error_msg.lower()
185
+
186
+
187
+ def test_manifest_to_context():
188
+ """Test conversion of manifest to policy context."""
189
+ engine = IATPPolicyEngine()
190
+
191
+ manifest = CapabilityManifest(
192
+ agent_id="test-agent",
193
+ trust_level=TrustLevel.VERIFIED_PARTNER,
194
+ capabilities=AgentCapabilities(
195
+ idempotency=True,
196
+ reversibility=ReversibilityLevel.PARTIAL,
197
+ ),
198
+ privacy_contract=PrivacyContract(
199
+ retention=RetentionPolicy.EPHEMERAL,
200
+ human_review=True,
201
+ encryption_at_rest=True,
202
+ encryption_in_transit=True,
203
+ )
204
+ )
205
+
206
+ context = engine._manifest_to_context(manifest)
207
+
208
+ assert context["agent_id"] == "test-agent"
209
+ assert context["trust_level"] == "verified_partner"
210
+ assert context["retention_policy"] == "ephemeral"
211
+ assert context["reversibility"] == "partial"
212
+ assert context["idempotency"] is True
213
+ assert context["human_review"] is True
214
+ assert context["encryption_at_rest"] is True
215
+ assert context["encryption_in_transit"] is True
216
+
217
+
218
+ def test_validate_handshake_with_required_scopes():
219
+ """Test handshake validation with required scopes."""
220
+ engine = IATPPolicyEngine()
221
+
222
+ manifest = CapabilityManifest(
223
+ agent_id="coder-agent",
224
+ trust_level=TrustLevel.TRUSTED,
225
+ capabilities=AgentCapabilities(
226
+ idempotency=True,
227
+ reversibility=ReversibilityLevel.FULL,
228
+ ),
229
+ privacy_contract=PrivacyContract(
230
+ retention=RetentionPolicy.EPHEMERAL,
231
+ human_review=False,
232
+ ),
233
+ scopes=["repo:read", "repo:write"]
234
+ )
235
+
236
+ is_compatible, error_msg = engine.validate_handshake(
237
+ manifest,
238
+ required_scopes=["repo:write"]
239
+ )
240
+
241
+ assert is_compatible is True
242
+ assert error_msg is None
243
+
244
+
245
+ def test_validate_handshake_missing_scopes():
246
+ """Test handshake validation with missing required scopes."""
247
+ engine = IATPPolicyEngine()
248
+
249
+ manifest = CapabilityManifest(
250
+ agent_id="reviewer-agent",
251
+ trust_level=TrustLevel.STANDARD,
252
+ capabilities=AgentCapabilities(
253
+ idempotency=True,
254
+ reversibility=ReversibilityLevel.FULL,
255
+ ),
256
+ privacy_contract=PrivacyContract(
257
+ retention=RetentionPolicy.TEMPORARY,
258
+ human_review=False,
259
+ ),
260
+ scopes=["repo:read"] # Only has read access
261
+ )
262
+
263
+ is_compatible, error_msg = engine.validate_handshake(
264
+ manifest,
265
+ required_scopes=["repo:write"] # But needs write access
266
+ )
267
+
268
+ assert is_compatible is False
269
+ assert error_msg is not None
270
+ assert "repo:write" in error_msg
271
+
272
+
273
+ def test_validate_handshake_multiple_scopes():
274
+ """Test handshake validation with multiple required scopes."""
275
+ engine = IATPPolicyEngine()
276
+
277
+ manifest = CapabilityManifest(
278
+ agent_id="admin-agent",
279
+ trust_level=TrustLevel.VERIFIED_PARTNER,
280
+ capabilities=AgentCapabilities(
281
+ idempotency=True,
282
+ reversibility=ReversibilityLevel.FULL,
283
+ ),
284
+ privacy_contract=PrivacyContract(
285
+ retention=RetentionPolicy.EPHEMERAL,
286
+ human_review=False,
287
+ ),
288
+ scopes=["repo:read", "repo:write", "admin:manage"]
289
+ )
290
+
291
+ is_compatible, error_msg = engine.validate_handshake(
292
+ manifest,
293
+ required_scopes=["repo:read", "repo:write"]
294
+ )
295
+
296
+ assert is_compatible is True
297
+ assert error_msg is None
298
+
299
+
300
+ def test_validate_handshake_no_scopes_required():
301
+ """Test handshake validation when no scopes are required."""
302
+ engine = IATPPolicyEngine()
303
+
304
+ manifest = CapabilityManifest(
305
+ agent_id="basic-agent",
306
+ trust_level=TrustLevel.STANDARD,
307
+ capabilities=AgentCapabilities(
308
+ idempotency=True,
309
+ reversibility=ReversibilityLevel.FULL,
310
+ ),
311
+ privacy_contract=PrivacyContract(
312
+ retention=RetentionPolicy.TEMPORARY,
313
+ human_review=False,
314
+ ),
315
+ scopes=[] # No scopes
316
+ )
317
+
318
+ is_compatible, error_msg = engine.validate_handshake(
319
+ manifest,
320
+ required_scopes=None # No scopes required
321
+ )
322
+
323
+ assert is_compatible is True
324
+ assert error_msg is None
325
+
326
+
327
+ def test_manifest_to_context_with_scopes():
328
+ """Test conversion of manifest with scopes to policy context."""
329
+ engine = IATPPolicyEngine()
330
+
331
+ manifest = CapabilityManifest(
332
+ agent_id="scoped-agent",
333
+ trust_level=TrustLevel.TRUSTED,
334
+ capabilities=AgentCapabilities(
335
+ idempotency=True,
336
+ reversibility=ReversibilityLevel.FULL,
337
+ ),
338
+ privacy_contract=PrivacyContract(
339
+ retention=RetentionPolicy.EPHEMERAL,
340
+ human_review=False,
341
+ ),
342
+ scopes=["repo:read", "repo:write"]
343
+ )
344
+
345
+ context = engine._manifest_to_context(manifest)
346
+
347
+ assert context["scopes"] == ["repo:read", "repo:write"]