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,232 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """Standalone circuit breaker implementation.
4
+
5
+ This module provides a self-contained circuit breaker that requires no
6
+ external packages beyond the Python standard library. It is used as a
7
+ fallback by ``agent_os.circuit_breaker`` when ``agent_sre`` is not installed.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import inspect
13
+ import threading
14
+ import time
15
+ from dataclasses import dataclass
16
+ from enum import Enum
17
+ from typing import Any
18
+
19
+
20
+ class CircuitState(str, Enum):
21
+ CLOSED = "CLOSED"
22
+ OPEN = "OPEN"
23
+ HALF_OPEN = "HALF_OPEN"
24
+
25
+
26
+ class CircuitOpenError(Exception):
27
+ """Raised when a call is attempted on an open circuit."""
28
+
29
+ def __init__(self, agent_id: str, retry_after: float) -> None:
30
+ self.agent_id = agent_id
31
+ self.retry_after = retry_after
32
+ super().__init__(
33
+ f"Circuit breaker OPEN for agent '{agent_id}'. "
34
+ f"Retry after {retry_after:.1f}s."
35
+ )
36
+
37
+
38
+ CircuitBreakerOpen = CircuitOpenError
39
+
40
+
41
+ @dataclass(init=False)
42
+ class CircuitBreakerConfig:
43
+ """Configuration for a circuit breaker instance."""
44
+
45
+ failure_threshold: int = 5
46
+ recovery_timeout_seconds: float = 30.0
47
+ half_open_max_calls: int = 1
48
+
49
+ def __init__(
50
+ self,
51
+ failure_threshold: int = 5,
52
+ recovery_timeout_seconds: float | None = None,
53
+ half_open_max_calls: int = 1,
54
+ reset_timeout_seconds: float | None = None,
55
+ ) -> None:
56
+ if (
57
+ recovery_timeout_seconds is not None
58
+ and reset_timeout_seconds is not None
59
+ and recovery_timeout_seconds != reset_timeout_seconds
60
+ ):
61
+ raise ValueError(
62
+ "recovery_timeout_seconds and reset_timeout_seconds must match"
63
+ )
64
+ timeout = recovery_timeout_seconds
65
+ if timeout is None:
66
+ timeout = reset_timeout_seconds
67
+ if timeout is None:
68
+ timeout = 30.0
69
+ self.failure_threshold = failure_threshold
70
+ self.recovery_timeout_seconds = timeout
71
+ self.half_open_max_calls = half_open_max_calls
72
+
73
+ @property
74
+ def reset_timeout_seconds(self) -> float:
75
+ return self.recovery_timeout_seconds
76
+
77
+ @reset_timeout_seconds.setter
78
+ def reset_timeout_seconds(self, value: float) -> None:
79
+ self.recovery_timeout_seconds = value
80
+
81
+
82
+ class CircuitBreaker:
83
+ """Standalone circuit breaker (used when agent_sre is not installed)."""
84
+
85
+ def __init__(
86
+ self,
87
+ agent_id: str | CircuitBreakerConfig | None = None,
88
+ config: CircuitBreakerConfig | None = None,
89
+ ) -> None:
90
+ if isinstance(agent_id, CircuitBreakerConfig) and config is None:
91
+ config = agent_id
92
+ agent_id = None
93
+ self.agent_id = agent_id or "legacy"
94
+ self.config = config or CircuitBreakerConfig()
95
+ self._config = self.config
96
+ self._state = CircuitState.CLOSED
97
+ self._failure_count = 0
98
+ self._success_count = 0
99
+ self._half_open_calls = 0
100
+ self._last_failure_time = 0.0
101
+ self._lock = threading.Lock()
102
+
103
+ @property
104
+ def state(self) -> str:
105
+ return self.get_state().value
106
+
107
+ @property
108
+ def failure_count(self) -> int:
109
+ return self._failure_count
110
+
111
+ def get_state(self) -> CircuitState:
112
+ with self._lock:
113
+ self._maybe_transition_to_half_open()
114
+ return self._state
115
+
116
+ def call(self, func: Any, *args: Any, fallback: Any = None, **kwargs: Any) -> Any:
117
+ retry_after = self._prepare_call()
118
+ if retry_after is not None:
119
+ if fallback is not None:
120
+ return fallback
121
+ raise CircuitOpenError(self.agent_id, retry_after)
122
+ try:
123
+ result = func(*args, **kwargs)
124
+ except Exception:
125
+ self.record_failure()
126
+ raise
127
+ if inspect.isawaitable(result):
128
+ async def _await_result() -> Any:
129
+ try:
130
+ value = await result
131
+ except Exception:
132
+ self.record_failure()
133
+ raise
134
+ self.record_success()
135
+ return value
136
+ return _await_result()
137
+ self.record_success()
138
+ return result
139
+
140
+ def record_success(self) -> None:
141
+ with self._lock:
142
+ if self._state is CircuitState.HALF_OPEN:
143
+ self._transition(CircuitState.CLOSED)
144
+ self._failure_count = 0
145
+ self._success_count += 1
146
+ self._half_open_calls = 0
147
+
148
+ def record_failure(self) -> None:
149
+ with self._lock:
150
+ self._failure_count += 1
151
+ self._last_failure_time = time.monotonic()
152
+ if self._state is CircuitState.HALF_OPEN:
153
+ self._transition(CircuitState.OPEN)
154
+ self._half_open_calls = 0
155
+ elif self._failure_count >= self.config.failure_threshold:
156
+ self._transition(CircuitState.OPEN)
157
+
158
+ def reset(self) -> None:
159
+ with self._lock:
160
+ self._transition(CircuitState.CLOSED)
161
+ self._failure_count = 0
162
+ self._success_count = 0
163
+ self._half_open_calls = 0
164
+ self._last_failure_time = 0.0
165
+
166
+ def _prepare_call(self) -> float | None:
167
+ with self._lock:
168
+ self._maybe_transition_to_half_open()
169
+ if self._state is CircuitState.OPEN:
170
+ return self._time_until_recovery()
171
+ if self._state is CircuitState.HALF_OPEN:
172
+ if self._half_open_calls >= self.config.half_open_max_calls:
173
+ return self._time_until_recovery()
174
+ self._half_open_calls += 1
175
+ return None
176
+
177
+ def _maybe_transition_to_half_open(self) -> None:
178
+ if self._state is CircuitState.OPEN:
179
+ elapsed = time.monotonic() - self._last_failure_time
180
+ if elapsed >= self.config.recovery_timeout_seconds:
181
+ self._transition(CircuitState.HALF_OPEN)
182
+
183
+ def _transition(self, new_state: CircuitState) -> None:
184
+ self._state = new_state
185
+ if new_state is CircuitState.HALF_OPEN:
186
+ self._half_open_calls = 0
187
+
188
+ def _time_until_recovery(self) -> float:
189
+ elapsed = time.monotonic() - self._last_failure_time
190
+ return max(0.0, self.config.recovery_timeout_seconds - elapsed)
191
+
192
+
193
+ class CascadeDetector:
194
+ """Detects cascading failures across multiple agents."""
195
+
196
+ def __init__(
197
+ self,
198
+ agents: list[str],
199
+ cascade_threshold: int = 3,
200
+ config: CircuitBreakerConfig | None = None,
201
+ ) -> None:
202
+ self.cascade_threshold = cascade_threshold
203
+ self._breakers: dict[str, CircuitBreaker] = {
204
+ agent_id: CircuitBreaker(agent_id, config) for agent_id in agents
205
+ }
206
+
207
+ def get_breaker(self, agent_id: str) -> CircuitBreaker | None:
208
+ return self._breakers.get(agent_id)
209
+
210
+ def check_cascade(self) -> bool:
211
+ return len(self.get_affected_agents()) >= self.cascade_threshold
212
+
213
+ def get_affected_agents(self) -> list[str]:
214
+ return [
215
+ agent_id
216
+ for agent_id, breaker in self._breakers.items()
217
+ if breaker.state == CircuitState.OPEN.value
218
+ ]
219
+
220
+ def reset_all(self) -> None:
221
+ for breaker in self._breakers.values():
222
+ breaker.reset()
223
+
224
+
225
+ __all__ = [
226
+ "CascadeDetector",
227
+ "CircuitBreaker",
228
+ "CircuitBreakerConfig",
229
+ "CircuitBreakerOpen",
230
+ "CircuitOpenError",
231
+ "CircuitState",
232
+ ]
@@ -0,0 +1,193 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """OpenTelemetry-friendly metrics helpers for MCP governance components."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import logging
8
+ from typing import Any, Protocol
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ try:
13
+ from opentelemetry import metrics as _otel_metrics
14
+
15
+ _HAS_OTEL = True
16
+ except ImportError: # pragma: no cover
17
+ _otel_metrics = None # type: ignore[assignment]
18
+ _HAS_OTEL = False
19
+
20
+
21
+ class MCPMetricsRecorder(Protocol):
22
+ """Protocol for MCP governance metric emission."""
23
+
24
+ def record_decision(
25
+ self,
26
+ *,
27
+ allowed: bool,
28
+ agent_id: str,
29
+ tool_name: str,
30
+ stage: str,
31
+ ) -> None:
32
+ """Record an allow or deny decision."""
33
+
34
+ def record_threats_detected(
35
+ self,
36
+ count: int,
37
+ *,
38
+ tool_name: str,
39
+ server_name: str,
40
+ ) -> None:
41
+ """Record detected threats from an MCP scan."""
42
+
43
+ def record_rate_limit_hit(self, *, agent_id: str, tool_name: str) -> None:
44
+ """Record a rate limit rejection."""
45
+
46
+ def record_scan(
47
+ self,
48
+ *,
49
+ operation: str,
50
+ tool_name: str,
51
+ server_name: str,
52
+ ) -> None:
53
+ """Record an MCP scan invocation."""
54
+
55
+
56
+ class NoOpMCPMetrics:
57
+ """No-op metrics recorder used when OTel is unavailable."""
58
+
59
+ def record_decision(
60
+ self,
61
+ *,
62
+ allowed: bool,
63
+ agent_id: str,
64
+ tool_name: str,
65
+ stage: str,
66
+ ) -> None:
67
+ return None
68
+
69
+ def record_threats_detected(
70
+ self,
71
+ count: int,
72
+ *,
73
+ tool_name: str,
74
+ server_name: str,
75
+ ) -> None:
76
+ return None
77
+
78
+ def record_rate_limit_hit(self, *, agent_id: str, tool_name: str) -> None:
79
+ return None
80
+
81
+ def record_scan(
82
+ self,
83
+ *,
84
+ operation: str,
85
+ tool_name: str,
86
+ server_name: str,
87
+ ) -> None:
88
+ return None
89
+
90
+
91
+ class MCPMetrics(NoOpMCPMetrics):
92
+ """MCP governance counters backed by OpenTelemetry when available."""
93
+
94
+ def __init__(self, meter_provider: Any | None = None) -> None:
95
+ self._enabled = _HAS_OTEL
96
+ self._decisions = None
97
+ self._threats_detected = None
98
+ self._rate_limit_hits = None
99
+ self._scans = None
100
+ if not _HAS_OTEL:
101
+ return
102
+
103
+ try:
104
+ if meter_provider is not None:
105
+ meter = meter_provider.get_meter("agent_os.mcp", version="3.1.0")
106
+ else:
107
+ meter = _otel_metrics.get_meter("agent_os.mcp", version="3.1.0")
108
+
109
+ self._decisions = meter.create_counter(
110
+ "mcp_decisions",
111
+ description="Total MCP gateway allow and deny decisions.",
112
+ )
113
+ self._threats_detected = meter.create_counter(
114
+ "mcp_threats_detected",
115
+ description="Threats detected by MCP scanners.",
116
+ )
117
+ self._rate_limit_hits = meter.create_counter(
118
+ "mcp_rate_limit_hits",
119
+ description="MCP requests denied by rate limiting.",
120
+ )
121
+ self._scans = meter.create_counter(
122
+ "mcp_scans",
123
+ description="MCP scan operations performed.",
124
+ )
125
+ except Exception: # pragma: no cover - defensive opt-in path
126
+ logger.debug("Failed to initialize MCP OpenTelemetry counters", exc_info=True)
127
+ self._enabled = False
128
+
129
+ def record_decision(
130
+ self,
131
+ *,
132
+ allowed: bool,
133
+ agent_id: str,
134
+ tool_name: str,
135
+ stage: str,
136
+ ) -> None:
137
+ if not self._enabled or self._decisions is None:
138
+ return
139
+ self._decisions.add(
140
+ 1,
141
+ {
142
+ "agent_id": agent_id,
143
+ "tool_name": tool_name,
144
+ "decision": "allow" if allowed else "deny",
145
+ "stage": stage,
146
+ },
147
+ )
148
+
149
+ def record_threats_detected(
150
+ self,
151
+ count: int,
152
+ *,
153
+ tool_name: str,
154
+ server_name: str,
155
+ ) -> None:
156
+ if count <= 0 or not self._enabled or self._threats_detected is None:
157
+ return
158
+ self._threats_detected.add(
159
+ count,
160
+ {
161
+ "tool_name": tool_name,
162
+ "server_name": server_name,
163
+ },
164
+ )
165
+
166
+ def record_rate_limit_hit(self, *, agent_id: str, tool_name: str) -> None:
167
+ if not self._enabled or self._rate_limit_hits is None:
168
+ return
169
+ self._rate_limit_hits.add(
170
+ 1,
171
+ {
172
+ "agent_id": agent_id,
173
+ "tool_name": tool_name,
174
+ },
175
+ )
176
+
177
+ def record_scan(
178
+ self,
179
+ *,
180
+ operation: str,
181
+ tool_name: str,
182
+ server_name: str,
183
+ ) -> None:
184
+ if not self._enabled or self._scans is None:
185
+ return
186
+ self._scans.add(
187
+ 1,
188
+ {
189
+ "operation": operation,
190
+ "tool_name": tool_name,
191
+ "server_name": server_name,
192
+ },
193
+ )
@@ -0,0 +1,20 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """Adversarial Evaluation — backward-compatibility shim.
4
+
5
+ Attempts to import the canonical implementation from
6
+ ``agent_sre.chaos.adversarial_policy``. When ``agent_sre`` is not
7
+ installed the standalone fallback in ``agent_os._adversarial_impl`` is
8
+ re-exported so that ``agent_os`` continues to work without requiring the
9
+ optional SRE package.
10
+
11
+ .. deprecated::
12
+ Import from ``agent_sre.chaos.adversarial_policy`` instead.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ try:
18
+ from agent_sre.chaos.adversarial_policy import * # noqa: F401,F403
19
+ except ImportError:
20
+ from agent_os._adversarial_impl import * # noqa: F401,F403