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,328 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """Tests for the Reputation Engine."""
4
+
5
+ import os
6
+ import sys
7
+ from datetime import datetime, timedelta, timezone
8
+
9
+ import pytest
10
+
11
+ _nexus_parent = os.path.join(os.path.dirname(__file__), "..", "..")
12
+ if _nexus_parent not in sys.path:
13
+ sys.path.insert(0, _nexus_parent)
14
+
15
+ from nexus.reputation import ReputationEngine, ReputationHistory, TrustScore, TrustTier, SlashEvent
16
+
17
+
18
+ class TestTrustTier:
19
+ """Tests for TrustScore.get_tier() classmethod."""
20
+
21
+ def test_verified_partner_at_900(self):
22
+ assert TrustScore.get_tier(900) == TrustTier.VERIFIED_PARTNER
23
+
24
+ def test_verified_partner_at_1000(self):
25
+ assert TrustScore.get_tier(1000) == TrustTier.VERIFIED_PARTNER
26
+
27
+ def test_trusted_at_700(self):
28
+ assert TrustScore.get_tier(700) == TrustTier.TRUSTED
29
+
30
+ def test_trusted_at_899(self):
31
+ assert TrustScore.get_tier(899) == TrustTier.TRUSTED
32
+
33
+ def test_standard_at_500(self):
34
+ assert TrustScore.get_tier(500) == TrustTier.STANDARD
35
+
36
+ def test_standard_at_699(self):
37
+ assert TrustScore.get_tier(699) == TrustTier.STANDARD
38
+
39
+ def test_probationary_at_300(self):
40
+ assert TrustScore.get_tier(300) == TrustTier.PROBATIONARY
41
+
42
+ def test_probationary_at_499(self):
43
+ assert TrustScore.get_tier(499) == TrustTier.PROBATIONARY
44
+
45
+ def test_untrusted_at_299(self):
46
+ assert TrustScore.get_tier(299) == TrustTier.UNTRUSTED
47
+
48
+ def test_untrusted_at_zero(self):
49
+ assert TrustScore.get_tier(0) == TrustTier.UNTRUSTED
50
+
51
+
52
+ class TestTrustScoreMeetsThreshold:
53
+ """Tests for TrustScore.meets_threshold()."""
54
+
55
+ def test_meets_when_equal(self):
56
+ ts = TrustScore(
57
+ agent_did="did:nexus:a", total_score=500, tier=TrustTier.STANDARD,
58
+ base_score=400, behavioral_modifier=100, capability_modifier=0,
59
+ )
60
+ assert ts.meets_threshold(500) is True
61
+
62
+ def test_meets_when_above(self):
63
+ ts = TrustScore(
64
+ agent_did="did:nexus:a", total_score=700, tier=TrustTier.TRUSTED,
65
+ base_score=600, behavioral_modifier=100, capability_modifier=0,
66
+ )
67
+ assert ts.meets_threshold(500) is True
68
+
69
+ def test_fails_when_below(self):
70
+ ts = TrustScore(
71
+ agent_did="did:nexus:a", total_score=300, tier=TrustTier.PROBATIONARY,
72
+ base_score=300, behavioral_modifier=0, capability_modifier=0,
73
+ )
74
+ assert ts.meets_threshold(500) is False
75
+
76
+
77
+ class TestReputationHistory:
78
+ """Tests for ReputationHistory properties."""
79
+
80
+ def test_success_rate_with_tasks(self):
81
+ h = ReputationHistory(
82
+ agent_did="did:nexus:a", successful_tasks=8, failed_tasks=2, total_tasks=10,
83
+ )
84
+ assert h.success_rate == pytest.approx(0.8)
85
+
86
+ def test_success_rate_no_tasks(self):
87
+ h = ReputationHistory(agent_did="did:nexus:a")
88
+ assert h.success_rate == 0.0
89
+
90
+ def test_dispute_win_rate_with_disputes(self):
91
+ h = ReputationHistory(
92
+ agent_did="did:nexus:a", disputes_won=3, disputes_lost=1,
93
+ )
94
+ assert h.dispute_win_rate == pytest.approx(0.75)
95
+
96
+ def test_dispute_win_rate_no_disputes(self):
97
+ h = ReputationHistory(agent_did="did:nexus:a")
98
+ assert h.dispute_win_rate == 0.5 # neutral default
99
+
100
+
101
+ class TestCalculateTrustScore:
102
+ """Tests for ReputationEngine.calculate_trust_score()."""
103
+
104
+ def test_unknown_verification_gives_low_base(self, reputation_engine):
105
+ history = ReputationHistory(agent_did="did:nexus:a")
106
+ score = reputation_engine.calculate_trust_score("unknown", history)
107
+ assert score.base_score == 100
108
+
109
+ def test_registered_verification(self, reputation_engine):
110
+ history = ReputationHistory(agent_did="did:nexus:a")
111
+ score = reputation_engine.calculate_trust_score("registered", history)
112
+ assert score.base_score == 400
113
+
114
+ def test_verified_verification(self, reputation_engine):
115
+ history = ReputationHistory(agent_did="did:nexus:a")
116
+ score = reputation_engine.calculate_trust_score("verified", history)
117
+ assert score.base_score == 650
118
+
119
+ def test_verified_partner_verification(self, reputation_engine):
120
+ history = ReputationHistory(agent_did="did:nexus:a")
121
+ score = reputation_engine.calculate_trust_score("verified_partner", history)
122
+ assert score.base_score == 800
123
+
124
+ def test_successful_tasks_increase_score(self, reputation_engine):
125
+ history = ReputationHistory(
126
+ agent_did="did:nexus:a", successful_tasks=50, total_tasks=50,
127
+ )
128
+ score = reputation_engine.calculate_trust_score("registered", history)
129
+ assert score.behavioral_modifier > 0
130
+
131
+ def test_failed_tasks_decrease_score(self, reputation_engine):
132
+ history = ReputationHistory(
133
+ agent_did="did:nexus:a", failed_tasks=10, total_tasks=10,
134
+ )
135
+ score = reputation_engine.calculate_trust_score("registered", history)
136
+ assert score.behavioral_modifier < 0
137
+
138
+ def test_disputes_lost_reduce_score(self, reputation_engine):
139
+ history = ReputationHistory(
140
+ agent_did="did:nexus:a", disputes_lost=3,
141
+ )
142
+ score = reputation_engine.calculate_trust_score("registered", history)
143
+ assert score.behavioral_modifier < 0
144
+
145
+ def test_capability_modifier_idempotency(self, reputation_engine):
146
+ history = ReputationHistory(agent_did="did:nexus:a")
147
+ score = reputation_engine.calculate_trust_score(
148
+ "registered", history, capabilities={"idempotency": True},
149
+ )
150
+ assert score.capability_modifier >= 20
151
+
152
+ def test_capability_modifier_reversibility_full(self, reputation_engine):
153
+ history = ReputationHistory(agent_did="did:nexus:a")
154
+ score = reputation_engine.calculate_trust_score(
155
+ "registered", history, capabilities={"reversibility": "full"},
156
+ )
157
+ assert score.capability_modifier >= 50
158
+
159
+ def test_privacy_ephemeral_boost(self, reputation_engine):
160
+ history = ReputationHistory(agent_did="did:nexus:a")
161
+ score = reputation_engine.calculate_trust_score(
162
+ "registered", history, privacy={"retention_policy": "ephemeral"},
163
+ )
164
+ assert score.capability_modifier >= 30
165
+
166
+ def test_score_clamped_to_0_1000(self, reputation_engine):
167
+ # Very bad history
168
+ history = ReputationHistory(
169
+ agent_did="did:nexus:a", failed_tasks=100, total_tasks=100,
170
+ disputes_lost=10, times_slashed=10,
171
+ )
172
+ score = reputation_engine.calculate_trust_score("unknown", history)
173
+ assert 0 <= score.total_score <= 1000
174
+
175
+ def test_score_has_correct_tier(self, reputation_engine):
176
+ history = ReputationHistory(agent_did="did:nexus:a")
177
+ score = reputation_engine.calculate_trust_score("verified_partner", history)
178
+ assert score.tier == TrustScore.get_tier(score.total_score)
179
+
180
+
181
+ class TestRecordTaskOutcome:
182
+ """Tests for ReputationEngine.record_task_outcome()."""
183
+
184
+ def test_success_increments(self, reputation_engine):
185
+ h = reputation_engine.record_task_outcome("did:nexus:a", "success")
186
+ assert h.successful_tasks == 1
187
+ assert h.total_tasks == 1
188
+
189
+ def test_failure_increments(self, reputation_engine):
190
+ h = reputation_engine.record_task_outcome("did:nexus:a", "failure")
191
+ assert h.failed_tasks == 1
192
+ assert h.total_tasks == 1
193
+
194
+ def test_partial_increments_both(self, reputation_engine):
195
+ h = reputation_engine.record_task_outcome("did:nexus:a", "partial")
196
+ assert h.successful_tasks == 0.5
197
+ assert h.failed_tasks == 0.5
198
+ assert h.total_tasks == 1
199
+
200
+ def test_multiple_outcomes_accumulate(self, reputation_engine):
201
+ reputation_engine.record_task_outcome("did:nexus:a", "success")
202
+ reputation_engine.record_task_outcome("did:nexus:a", "success")
203
+ h = reputation_engine.record_task_outcome("did:nexus:a", "failure")
204
+ assert h.successful_tasks == 2
205
+ assert h.failed_tasks == 1
206
+ assert h.total_tasks == 3
207
+
208
+
209
+ class TestRecordDisputeOutcome:
210
+ """Tests for ReputationEngine.record_dispute_outcome()."""
211
+
212
+ def test_won_increments(self, reputation_engine):
213
+ h = reputation_engine.record_dispute_outcome("did:nexus:a", "won")
214
+ assert h.disputes_won == 1
215
+
216
+ def test_lost_increments(self, reputation_engine):
217
+ h = reputation_engine.record_dispute_outcome("did:nexus:a", "lost")
218
+ assert h.disputes_lost == 1
219
+
220
+
221
+ class TestSlashReputation:
222
+ """Tests for ReputationEngine.slash_reputation()."""
223
+
224
+ def test_critical_slash_penalty(self, reputation_engine):
225
+ event = reputation_engine.slash_reputation(
226
+ "did:nexus:a", reason="fraud", severity="critical",
227
+ )
228
+ assert event.score_reduction == 200
229
+
230
+ def test_high_slash_penalty(self, reputation_engine):
231
+ event = reputation_engine.slash_reputation(
232
+ "did:nexus:a", reason="hallucination", severity="high",
233
+ )
234
+ assert event.score_reduction == 100
235
+
236
+ def test_medium_slash_penalty(self, reputation_engine):
237
+ event = reputation_engine.slash_reputation(
238
+ "did:nexus:a", reason="timeout", severity="medium",
239
+ )
240
+ assert event.score_reduction == 50
241
+
242
+ def test_low_slash_penalty(self, reputation_engine):
243
+ event = reputation_engine.slash_reputation(
244
+ "did:nexus:a", reason="policy_violation", severity="low",
245
+ )
246
+ assert event.score_reduction == 25
247
+
248
+ def test_slash_updates_history(self, reputation_engine):
249
+ reputation_engine.slash_reputation("did:nexus:a", reason="fraud", severity="critical")
250
+ h = reputation_engine._get_or_create_history("did:nexus:a")
251
+ assert h.times_slashed == 1
252
+ assert h.total_slash_amount == 200
253
+
254
+ def test_slash_with_evidence(self, reputation_engine):
255
+ event = reputation_engine.slash_reputation(
256
+ "did:nexus:a", reason="fraud", severity="high",
257
+ evidence_hash="ev_hash_123", trace_id="trace_456",
258
+ )
259
+ assert event.evidence_hash == "ev_hash_123"
260
+ assert event.trace_id == "trace_456"
261
+
262
+ def test_slash_broadcast_flag(self, reputation_engine):
263
+ event = reputation_engine.slash_reputation(
264
+ "did:nexus:a", reason="fraud", severity="high", broadcast=False,
265
+ )
266
+ assert event.broadcast_to_network is False
267
+ assert event.broadcast_at is None
268
+
269
+ def test_slash_score_after_not_negative(self, reputation_engine):
270
+ event = reputation_engine.slash_reputation(
271
+ "did:nexus:a", reason="fraud", severity="critical",
272
+ )
273
+ assert event.score_after >= 0
274
+
275
+
276
+ class TestCheckTrustThreshold:
277
+ """Tests for ReputationEngine.check_trust_threshold()."""
278
+
279
+ def test_new_agent_below_default_threshold(self, reputation_engine):
280
+ meets, score = reputation_engine.check_trust_threshold("did:nexus:new")
281
+ assert score.total_score == 400 # registered base
282
+ assert meets is False # 400 < 500
283
+
284
+ def test_custom_threshold(self, reputation_engine):
285
+ meets, score = reputation_engine.check_trust_threshold("did:nexus:a", required_score=300)
286
+ assert meets is True # 400 >= 300
287
+
288
+
289
+ class TestLeaderboard:
290
+ """Tests for ReputationEngine.get_leaderboard()."""
291
+
292
+ def test_empty_leaderboard(self, reputation_engine):
293
+ assert reputation_engine.get_leaderboard() == []
294
+
295
+ def test_leaderboard_sorted_descending(self, reputation_engine):
296
+ # Populate cache via check_trust_threshold
297
+ reputation_engine.check_trust_threshold("did:nexus:a")
298
+ reputation_engine.check_trust_threshold("did:nexus:b")
299
+ board = reputation_engine.get_leaderboard()
300
+ assert len(board) == 2
301
+ for i in range(len(board) - 1):
302
+ assert board[i].total_score >= board[i + 1].total_score
303
+
304
+ def test_leaderboard_limit(self, reputation_engine):
305
+ for i in range(5):
306
+ reputation_engine.check_trust_threshold(f"did:nexus:agent-{i}")
307
+ board = reputation_engine.get_leaderboard(limit=3)
308
+ assert len(board) == 3
309
+
310
+
311
+ class TestSlashHistory:
312
+ """Tests for ReputationEngine.get_slash_history()."""
313
+
314
+ def test_empty_history(self, reputation_engine):
315
+ assert reputation_engine.get_slash_history() == []
316
+
317
+ def test_filter_by_agent(self, reputation_engine):
318
+ reputation_engine.slash_reputation("did:nexus:a", reason="fraud", severity="high")
319
+ reputation_engine.slash_reputation("did:nexus:b", reason="timeout", severity="low")
320
+ events = reputation_engine.get_slash_history(agent_did="did:nexus:a")
321
+ assert len(events) == 1
322
+ assert events[0].agent_did == "did:nexus:a"
323
+
324
+ def test_filter_by_since(self, reputation_engine):
325
+ reputation_engine.slash_reputation("did:nexus:a", reason="fraud", severity="high")
326
+ future = datetime.now(timezone.utc) + timedelta(hours=1)
327
+ events = reputation_engine.get_slash_history(since=future)
328
+ assert len(events) == 0
@@ -0,0 +1,295 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """Tests for Nexus schema models."""
4
+
5
+ import os
6
+ import sys
7
+ from datetime import datetime, timedelta, timezone
8
+
9
+ import pytest
10
+
11
+ _nexus_parent = os.path.join(os.path.dirname(__file__), "..", "..")
12
+ if _nexus_parent not in sys.path:
13
+ sys.path.insert(0, _nexus_parent)
14
+
15
+ from nexus.schemas.manifest import (
16
+ AgentIdentity,
17
+ AgentCapabilities,
18
+ AgentPrivacy,
19
+ MuteRules,
20
+ AgentManifest,
21
+ )
22
+ from nexus.schemas.escrow import EscrowRequest, EscrowReceipt, EscrowStatus
23
+ from nexus.schemas.receipt import JobReceipt, JobCompletionReceipt, SignedReceipt
24
+ from nexus.schemas.compliance import ComplianceRecord, ComplianceAuditReport
25
+
26
+
27
+ class TestAgentIdentity:
28
+ """Tests for AgentIdentity DID format validation."""
29
+
30
+ def test_valid_did(self):
31
+ identity = AgentIdentity(
32
+ did="did:nexus:my-agent", verification_key="ed25519:key123", owner_id="org",
33
+ )
34
+ assert identity.did == "did:nexus:my-agent"
35
+
36
+ def test_invalid_did_prefix(self):
37
+ with pytest.raises(Exception):
38
+ AgentIdentity(
39
+ did="did:other:my-agent", verification_key="ed25519:key123", owner_id="org",
40
+ )
41
+
42
+ def test_invalid_verification_key(self):
43
+ with pytest.raises(Exception):
44
+ AgentIdentity(
45
+ did="did:nexus:my-agent", verification_key="rsa:key123", owner_id="org",
46
+ )
47
+
48
+ def test_valid_ed25519_key(self):
49
+ identity = AgentIdentity(
50
+ did="did:nexus:agent", verification_key="ed25519:abc", owner_id="org",
51
+ )
52
+ assert identity.verification_key == "ed25519:abc"
53
+
54
+
55
+ class TestAgentCapabilities:
56
+ """Tests for AgentCapabilities constraints."""
57
+
58
+ def test_max_concurrency_min(self):
59
+ with pytest.raises(Exception):
60
+ AgentCapabilities(max_concurrency=0)
61
+
62
+ def test_max_concurrency_max(self):
63
+ with pytest.raises(Exception):
64
+ AgentCapabilities(max_concurrency=1001)
65
+
66
+ def test_max_concurrency_valid(self):
67
+ cap = AgentCapabilities(max_concurrency=500)
68
+ assert cap.max_concurrency == 500
69
+
70
+ def test_sla_latency_min(self):
71
+ with pytest.raises(Exception):
72
+ AgentCapabilities(sla_latency_ms=50) # min 100
73
+
74
+ def test_sla_latency_max(self):
75
+ with pytest.raises(Exception):
76
+ AgentCapabilities(sla_latency_ms=500000) # max 300000
77
+
78
+ def test_sla_latency_valid(self):
79
+ cap = AgentCapabilities(sla_latency_ms=10000)
80
+ assert cap.sla_latency_ms == 10000
81
+
82
+ def test_defaults(self):
83
+ cap = AgentCapabilities()
84
+ assert cap.max_concurrency == 10
85
+ assert cap.idempotency is False
86
+ assert cap.reversibility == "partial"
87
+
88
+
89
+ class TestAgentManifest:
90
+ """Tests for AgentManifest creation."""
91
+
92
+ def test_valid_manifest(self, sample_manifest):
93
+ assert sample_manifest.identity.did == "did:nexus:test-agent-v1"
94
+ assert sample_manifest.verification_level == "registered"
95
+
96
+ def test_is_attestation_valid_false_by_default(self, sample_manifest):
97
+ assert sample_manifest.is_attestation_valid() is False
98
+
99
+ def test_is_attestation_valid_when_set(self):
100
+ m = AgentManifest(
101
+ identity=AgentIdentity(
102
+ did="did:nexus:a", verification_key="ed25519:k", owner_id="org",
103
+ ),
104
+ attestation_signature="sig_123",
105
+ attestation_expires=datetime.now(timezone.utc) + timedelta(hours=24),
106
+ )
107
+ assert m.is_attestation_valid() is True
108
+
109
+ def test_is_attestation_expired(self):
110
+ m = AgentManifest(
111
+ identity=AgentIdentity(
112
+ did="did:nexus:a", verification_key="ed25519:k", owner_id="org",
113
+ ),
114
+ attestation_signature="sig_123",
115
+ attestation_expires=datetime.now(timezone.utc) - timedelta(hours=1),
116
+ )
117
+ assert m.is_attestation_valid() is False
118
+
119
+ def test_to_iatp_manifest(self, sample_manifest):
120
+ iatp = sample_manifest.to_iatp_manifest()
121
+ assert iatp["$schema"].startswith("https://")
122
+ assert iatp["identity"]["verification_key"] == "ed25519:testkey123abc"
123
+
124
+
125
+ class TestEscrowRequestConstraints:
126
+ """Tests for EscrowRequest validation."""
127
+
128
+ def test_credits_must_be_positive(self):
129
+ with pytest.raises(Exception):
130
+ EscrowRequest(
131
+ requester_did="did:nexus:a", provider_did="did:nexus:b",
132
+ task_hash="h", credits=-1,
133
+ )
134
+
135
+ def test_credits_max_10000(self):
136
+ with pytest.raises(Exception):
137
+ EscrowRequest(
138
+ requester_did="did:nexus:a", provider_did="did:nexus:b",
139
+ task_hash="h", credits=10001,
140
+ )
141
+
142
+ def test_timeout_min_60(self):
143
+ with pytest.raises(Exception):
144
+ EscrowRequest(
145
+ requester_did="did:nexus:a", provider_did="did:nexus:b",
146
+ task_hash="h", credits=100, timeout_seconds=30,
147
+ )
148
+
149
+ def test_timeout_max_86400(self):
150
+ with pytest.raises(Exception):
151
+ EscrowRequest(
152
+ requester_did="did:nexus:a", provider_did="did:nexus:b",
153
+ task_hash="h", credits=100, timeout_seconds=100000,
154
+ )
155
+
156
+
157
+ class TestJobReceipt:
158
+ """Tests for JobReceipt.compute_hash()."""
159
+
160
+ def test_compute_hash_deterministic(self):
161
+ now = datetime(2024, 1, 1, 12, 0, 0)
162
+ r1 = JobReceipt(
163
+ receipt_id="r1", task_id="t1", requester_did="did:nexus:a",
164
+ provider_did="did:nexus:b", task_hash="hash1", created_at=now,
165
+ )
166
+ r2 = JobReceipt(
167
+ receipt_id="r1", task_id="t1", requester_did="did:nexus:a",
168
+ provider_did="did:nexus:b", task_hash="hash1", created_at=now,
169
+ )
170
+ assert r1.compute_hash() == r2.compute_hash()
171
+
172
+ def test_compute_hash_differs_for_different_data(self):
173
+ now = datetime(2024, 1, 1, 12, 0, 0)
174
+ r1 = JobReceipt(
175
+ receipt_id="r1", task_id="t1", requester_did="did:nexus:a",
176
+ provider_did="did:nexus:b", task_hash="hash1", created_at=now,
177
+ )
178
+ r2 = JobReceipt(
179
+ receipt_id="r2", task_id="t1", requester_did="did:nexus:a",
180
+ provider_did="did:nexus:b", task_hash="hash1", created_at=now,
181
+ )
182
+ assert r1.compute_hash() != r2.compute_hash()
183
+
184
+ def test_hash_is_sha256(self):
185
+ r = JobReceipt(
186
+ receipt_id="r1", task_id="t1", requester_did="did:nexus:a",
187
+ provider_did="did:nexus:b", task_hash="hash1",
188
+ )
189
+ h = r.compute_hash()
190
+ assert len(h) == 64 # SHA-256 hex digest length
191
+
192
+
193
+ class TestSignedReceipt:
194
+ """Tests for SignedReceipt methods."""
195
+
196
+ def _make_signed_receipt(self, req_sig=None, prov_sig=None, nexus_witnessed=False, nexus_sig=None):
197
+ receipt = JobCompletionReceipt(
198
+ receipt_id="r1", task_id="t1", requester_did="did:nexus:a",
199
+ provider_did="did:nexus:b", task_hash="h", outcome="success",
200
+ duration_ms=100,
201
+ )
202
+ return SignedReceipt(
203
+ receipt=receipt,
204
+ receipt_hash=receipt.compute_hash(),
205
+ requester_signature=req_sig,
206
+ provider_signature=prov_sig,
207
+ nexus_witnessed=nexus_witnessed,
208
+ nexus_signature=nexus_sig,
209
+ )
210
+
211
+ def test_is_fully_signed_both(self):
212
+ sr = self._make_signed_receipt(req_sig="sig_r", prov_sig="sig_p")
213
+ assert sr.is_fully_signed() is True
214
+
215
+ def test_is_not_fully_signed_missing_provider(self):
216
+ sr = self._make_signed_receipt(req_sig="sig_r")
217
+ assert sr.is_fully_signed() is False
218
+
219
+ def test_is_not_fully_signed_missing_requester(self):
220
+ sr = self._make_signed_receipt(prov_sig="sig_p")
221
+ assert sr.is_fully_signed() is False
222
+
223
+ def test_is_nexus_witnessed(self):
224
+ sr = self._make_signed_receipt(nexus_witnessed=True, nexus_sig="sig_n")
225
+ assert sr.is_nexus_witnessed() is True
226
+
227
+ def test_is_not_nexus_witnessed_no_sig(self):
228
+ sr = self._make_signed_receipt(nexus_witnessed=True, nexus_sig=None)
229
+ assert sr.is_nexus_witnessed() is False
230
+
231
+ def test_is_not_nexus_witnessed_flag_false(self):
232
+ sr = self._make_signed_receipt(nexus_witnessed=False, nexus_sig="sig_n")
233
+ assert sr.is_nexus_witnessed() is False
234
+
235
+
236
+ class TestComplianceRecord:
237
+ """Tests for ComplianceRecord.compute_hash()."""
238
+
239
+ def test_compute_hash_deterministic(self):
240
+ now = datetime(2024, 6, 1, 12, 0, 0)
241
+ r1 = ComplianceRecord(event_id="e1", event_type="agent_registered", timestamp=now)
242
+ r2 = ComplianceRecord(event_id="e1", event_type="agent_registered", timestamp=now)
243
+ assert r1.compute_hash() == r2.compute_hash()
244
+
245
+ def test_compute_hash_excludes_signature(self):
246
+ now = datetime(2024, 6, 1, 12, 0, 0)
247
+ r1 = ComplianceRecord(event_id="e1", event_type="agent_registered", timestamp=now)
248
+ r2 = ComplianceRecord(
249
+ event_id="e1", event_type="agent_registered", timestamp=now, signature="sig",
250
+ )
251
+ assert r1.compute_hash() == r2.compute_hash()
252
+
253
+
254
+ class TestEscrowReceiptMethods:
255
+ """Tests for EscrowReceipt.is_expired() and is_active()."""
256
+
257
+ def _make_receipt(self, status=EscrowStatus.PENDING, expires_delta_hours=1):
258
+ return EscrowReceipt(
259
+ escrow_id="e1",
260
+ request=EscrowRequest(
261
+ requester_did="did:nexus:a", provider_did="did:nexus:b",
262
+ task_hash="h", credits=100,
263
+ ),
264
+ status=status,
265
+ expires_at=datetime.now(timezone.utc) + timedelta(hours=expires_delta_hours),
266
+ requester_signature="sig",
267
+ )
268
+
269
+ def test_not_expired(self):
270
+ r = self._make_receipt(expires_delta_hours=1)
271
+ assert r.is_expired() is False
272
+
273
+ def test_expired(self):
274
+ r = self._make_receipt(expires_delta_hours=-1)
275
+ assert r.is_expired() is True
276
+
277
+ def test_active_pending(self):
278
+ r = self._make_receipt(status=EscrowStatus.PENDING)
279
+ assert r.is_active() is True
280
+
281
+ def test_active_active(self):
282
+ r = self._make_receipt(status=EscrowStatus.ACTIVE)
283
+ assert r.is_active() is True
284
+
285
+ def test_active_awaiting(self):
286
+ r = self._make_receipt(status=EscrowStatus.AWAITING_VALIDATION)
287
+ assert r.is_active() is True
288
+
289
+ def test_not_active_released(self):
290
+ r = self._make_receipt(status=EscrowStatus.RELEASED)
291
+ assert r.is_active() is False
292
+
293
+ def test_not_active_refunded(self):
294
+ r = self._make_receipt(status=EscrowStatus.REFUNDED)
295
+ assert r.is_active() is False