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,394 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """
4
+ Guardrails AI Bridge for Agent-OS
5
+ ===================================
6
+
7
+ Bridges Guardrails AI validators with Agent-OS policy enforcement.
8
+
9
+ Agent-OS enforces your Guardrails AI validators at the kernel level —
10
+ policy violations trigger Agent-OS signals (SIGKILL, SIGPOLICYVIOLATION).
11
+
12
+ Works without importing guardrails — uses a Protocol interface so you can
13
+ plug in any validator that implements ``validate(value) -> ValidationOutcome``.
14
+
15
+ Example:
16
+ >>> from agent_os.integrations.guardrails_adapter import GuardrailsKernel
17
+ >>>
18
+ >>> kernel = GuardrailsKernel(
19
+ ... validators=[PIIValidator(), ToxicityValidator()],
20
+ ... on_fail="block", # or "warn", "fix"
21
+ ... )
22
+ >>>
23
+ >>> result = kernel.validate_input("My SSN is 123-45-6789")
24
+ >>> assert not result.passed # PII detected
25
+ >>>
26
+ >>> result = kernel.validate_output("Safe response text")
27
+ >>> assert result.passed
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import logging
33
+ import time
34
+ from dataclasses import dataclass, field
35
+ from enum import Enum
36
+ from typing import Any, Callable, Protocol, runtime_checkable
37
+
38
+ logger = logging.getLogger(__name__)
39
+
40
+
41
+ # ------------------------------------------------------------------
42
+ # Validator Protocol (no guardrails import required)
43
+ # ------------------------------------------------------------------
44
+
45
+
46
+ class FailAction(str, Enum):
47
+ """What to do when a validator fails."""
48
+
49
+ BLOCK = "block"
50
+ WARN = "warn"
51
+ FIX = "fix"
52
+
53
+
54
+ @runtime_checkable
55
+ class ValidatorProtocol(Protocol):
56
+ """
57
+ Protocol for Guardrails AI validators (or any compatible validator).
58
+
59
+ A validator must implement ``validate(value, metadata)`` and return
60
+ a ``ValidationResult``-like object with ``outcome`` and ``error_message``.
61
+ """
62
+
63
+ @property
64
+ def name(self) -> str: ...
65
+
66
+ def validate(self, value: str, metadata: dict[str, Any] | None = None) -> Any: ...
67
+
68
+
69
+ @dataclass
70
+ class ValidationOutcome:
71
+ """Result of a single validator check."""
72
+
73
+ validator_name: str
74
+ passed: bool
75
+ error_message: str = ""
76
+ fixed_value: str | None = None
77
+ metadata: dict[str, Any] = field(default_factory=dict)
78
+
79
+ def to_dict(self) -> dict[str, Any]:
80
+ """Serialise this outcome to a plain dictionary.
81
+
82
+ Returns:
83
+ A dict with validator, passed, and optionally error
84
+ and fixed_value keys.
85
+ """
86
+ d: dict[str, Any] = {
87
+ "validator": self.validator_name,
88
+ "passed": self.passed,
89
+ }
90
+ if self.error_message:
91
+ d["error"] = self.error_message
92
+ if self.fixed_value is not None:
93
+ d["fixed_value"] = self.fixed_value
94
+ return d
95
+
96
+
97
+ @dataclass
98
+ class ValidationResult:
99
+ """Aggregated result across all validators."""
100
+
101
+ passed: bool
102
+ outcomes: list[ValidationOutcome]
103
+ original_value: str
104
+ final_value: str
105
+ action_taken: FailAction
106
+ timestamp: float = field(default_factory=time.time)
107
+
108
+ @property
109
+ def failed_validators(self) -> list[str]:
110
+ """Return the names of all validators that did not pass.
111
+
112
+ Returns:
113
+ List of validator name strings where passed is False.
114
+ """
115
+ return [o.validator_name for o in self.outcomes if not o.passed]
116
+
117
+ def to_dict(self) -> dict[str, Any]:
118
+ """Serialise this aggregated result to a plain dictionary.
119
+
120
+ Returns:
121
+ A dict with passed, action, outcomes, and failed_validators keys.
122
+ """
123
+ return {
124
+ "passed": self.passed,
125
+ "action": self.action_taken.value,
126
+ "outcomes": [o.to_dict() for o in self.outcomes],
127
+ "failed_validators": self.failed_validators,
128
+ }
129
+
130
+
131
+ # ------------------------------------------------------------------
132
+ # Built-in simple validators (no guardrails dependency)
133
+ # ------------------------------------------------------------------
134
+
135
+
136
+ class RegexValidator:
137
+ """Block content matching regex patterns."""
138
+
139
+ def __init__(self, patterns: list[str], validator_name: str = "regex"):
140
+ import re
141
+
142
+ self._patterns = [re.compile(p, re.IGNORECASE) for p in patterns]
143
+ self._name = validator_name
144
+
145
+ @property
146
+ def name(self) -> str:
147
+ """Return the human-readable name of this regex validator.
148
+
149
+ Returns:
150
+ The validator name string used in audit logs and outcomes.
151
+ """
152
+ return self._name
153
+
154
+ def validate(self, value: str, metadata: dict[str, Any] | None = None) -> ValidationOutcome:
155
+ """Validate a string by checking it against blocked regex patterns.
156
+
157
+ Args:
158
+ value: The text to scan.
159
+ metadata: Optional dict of additional context (unused).
160
+
161
+ Returns:
162
+ ValidationOutcome indicating pass or fail.
163
+ """
164
+
165
+ for pattern in self._patterns:
166
+ match = pattern.search(value)
167
+ if match:
168
+ return ValidationOutcome(
169
+ validator_name=self._name,
170
+ passed=False,
171
+ error_message=f"Content matches blocked pattern: {match.group()}",
172
+ )
173
+ return ValidationOutcome(validator_name=self._name, passed=True)
174
+
175
+
176
+ class LengthValidator:
177
+ """Enforce content length limits."""
178
+
179
+ def __init__(self, max_length: int = 10000, validator_name: str = "length"):
180
+ self._max_length = max_length
181
+ self._name = validator_name
182
+
183
+ @property
184
+ def name(self) -> str:
185
+ """Return the human-readable name of this length validator.
186
+
187
+ Returns:
188
+ The validator name string used in audit logs and outcomes.
189
+ """
190
+ return self._name
191
+
192
+ def validate(self, value: str, metadata: dict[str, Any] | None = None) -> ValidationOutcome:
193
+ """Validate that a string does not exceed the configured max length.
194
+
195
+ Args:
196
+ value: The text to check.
197
+ metadata: Optional dict of additional context (unused).
198
+
199
+ Returns:
200
+ ValidationOutcome with a fixed_value truncated to max_length on fail.
201
+ """
202
+ if len(value) > self._max_length:
203
+ return ValidationOutcome(
204
+ validator_name=self._name,
205
+ passed=False,
206
+ error_message=f"Content length {len(value)} exceeds max {self._max_length}",
207
+ fixed_value=value[: self._max_length],
208
+ )
209
+ return ValidationOutcome(validator_name=self._name, passed=True)
210
+
211
+
212
+ class KeywordValidator:
213
+ """Block content containing specific keywords."""
214
+
215
+ def __init__(self, blocked_keywords: list[str], validator_name: str = "keywords"):
216
+ self._keywords = [k.lower() for k in blocked_keywords]
217
+ self._name = validator_name
218
+
219
+ @property
220
+ def name(self) -> str:
221
+ """Return the human-readable name of this keyword validator.
222
+
223
+ Returns:
224
+ The validator name string used in audit logs and outcomes.
225
+ """
226
+ return self._name
227
+
228
+ def validate(self, value: str, metadata: dict[str, Any] | None = None) -> ValidationOutcome:
229
+ """Validate that a string contains none of the blocked keywords.
230
+
231
+ Args:
232
+ value: The text to scan (case-insensitive).
233
+ metadata: Optional dict of additional context (unused).
234
+
235
+ Returns:
236
+ ValidationOutcome indicating pass or fail.
237
+ """
238
+ value_lower = value.lower()
239
+ for kw in self._keywords:
240
+ if kw in value_lower:
241
+ return ValidationOutcome(
242
+ validator_name=self._name,
243
+ passed=False,
244
+ error_message=f"Content contains blocked keyword: '{kw}'",
245
+ )
246
+ return ValidationOutcome(validator_name=self._name, passed=True)
247
+
248
+
249
+ # ------------------------------------------------------------------
250
+ # Guardrails Kernel
251
+ # ------------------------------------------------------------------
252
+
253
+
254
+ class GuardrailsKernel:
255
+ """
256
+ Agent-OS governance kernel backed by Guardrails AI validators.
257
+
258
+ Validates inputs and outputs against a chain of validators.
259
+ Failed validations are recorded and trigger configurable actions.
260
+ """
261
+
262
+ def __init__(
263
+ self,
264
+ validators: list[Any] | None = None,
265
+ on_fail: str = "block",
266
+ on_violation: Callable[[ValidationResult], None] | None = None,
267
+ ):
268
+ self._validators: list[Any] = validators or []
269
+ self.on_fail = FailAction(on_fail)
270
+ self.on_violation = on_violation or self._default_violation_handler
271
+ self._history: list[ValidationResult] = []
272
+
273
+ def _default_violation_handler(self, result: ValidationResult) -> None:
274
+ """Default handler called when one or more validators fail.
275
+
276
+ Logs a warning for each failed validator name. Override by
277
+ passing a custom on_violation callable to GuardrailsKernel.
278
+
279
+ Args:
280
+ result: The aggregated ValidationResult.
281
+ """
282
+ for name in result.failed_validators:
283
+ logger.warning(f"Guardrail violation: {name}")
284
+
285
+ def add_validator(self, validator: Any) -> None:
286
+ """Add a validator to the chain."""
287
+ self._validators.append(validator)
288
+
289
+ def _run_validators(self, value: str) -> list[ValidationOutcome]:
290
+ """Run all validators against a value."""
291
+ outcomes = []
292
+ for v in self._validators:
293
+ try:
294
+ result = v.validate(value)
295
+ # Handle both our ValidationOutcome and Guardrails AI objects
296
+ if isinstance(result, ValidationOutcome):
297
+ outcomes.append(result)
298
+ else:
299
+ # Duck-type: expect .outcome / .validated_output / .error_message
300
+ passed = getattr(result, "outcome", "pass") == "pass"
301
+ error_msg = getattr(result, "error_message", "")
302
+ fixed = getattr(result, "validated_output", None)
303
+ outcomes.append(
304
+ ValidationOutcome(
305
+ validator_name=getattr(v, "name", type(v).__name__),
306
+ passed=passed,
307
+ error_message=str(error_msg) if error_msg else "",
308
+ fixed_value=fixed,
309
+ )
310
+ )
311
+ except Exception as e:
312
+ outcomes.append(
313
+ ValidationOutcome(
314
+ validator_name=getattr(v, "name", type(v).__name__),
315
+ passed=False,
316
+ error_message=f"Validator error: {e}",
317
+ )
318
+ )
319
+ return outcomes
320
+
321
+ def validate(self, value: str) -> ValidationResult:
322
+ """
323
+ Validate a value against all validators.
324
+
325
+ Returns a ValidationResult with aggregated outcomes and the action taken.
326
+ """
327
+ outcomes = self._run_validators(value)
328
+ all_passed = all(o.passed for o in outcomes)
329
+ final_value = value
330
+
331
+ action = FailAction.BLOCK # default
332
+ if all_passed:
333
+ action = FailAction.BLOCK # no action needed
334
+ else:
335
+ action = self.on_fail
336
+ if action == FailAction.FIX:
337
+ # Apply fixes from validators that provide them
338
+ for o in outcomes:
339
+ if not o.passed and o.fixed_value is not None:
340
+ final_value = o.fixed_value
341
+
342
+ result = ValidationResult(
343
+ passed=all_passed,
344
+ outcomes=outcomes,
345
+ original_value=value,
346
+ final_value=final_value,
347
+ action_taken=action if not all_passed else FailAction.BLOCK,
348
+ )
349
+ self._history.append(result)
350
+
351
+ if not all_passed:
352
+ self.on_violation(result)
353
+
354
+ return result
355
+
356
+ def validate_input(self, text: str) -> ValidationResult:
357
+ """Validate agent input (user query, tool arguments, etc.)."""
358
+ return self.validate(text)
359
+
360
+ def validate_output(self, text: str) -> ValidationResult:
361
+ """Validate agent output (response text, tool results, etc.)."""
362
+ return self.validate(text)
363
+
364
+ def get_history(self) -> list[ValidationResult]:
365
+ """Return all validation results."""
366
+ return list(self._history)
367
+
368
+ def get_stats(self) -> dict[str, Any]:
369
+ """Return guardrails statistics."""
370
+ total = len(self._history)
371
+ passed = sum(1 for r in self._history if r.passed)
372
+ return {
373
+ "total_validations": total,
374
+ "passed": passed,
375
+ "failed": total - passed,
376
+ "pass_rate": passed / total if total > 0 else 1.0,
377
+ "validators": [getattr(v, "name", type(v).__name__) for v in self._validators],
378
+ }
379
+
380
+ def reset(self) -> None:
381
+ """Clear validation history."""
382
+ self._history.clear()
383
+
384
+
385
+ __all__ = [
386
+ "GuardrailsKernel",
387
+ "ValidationResult",
388
+ "ValidationOutcome",
389
+ "FailAction",
390
+ "ValidatorProtocol",
391
+ "RegexValidator",
392
+ "LengthValidator",
393
+ "KeywordValidator",
394
+ ]
@@ -0,0 +1,197 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """
4
+ Health Check Endpoints for K8s Readiness/Liveness Probes
5
+
6
+ Thread-safe health checker with configurable component checks,
7
+ JSON-serializable reports, and aggregate status computation.
8
+ """
9
+
10
+ import threading
11
+ import time
12
+ from dataclasses import dataclass
13
+ from datetime import datetime, timezone
14
+ from enum import Enum
15
+ from typing import Callable
16
+
17
+
18
+ class HealthStatus(Enum):
19
+ """Possible health states for a component or the overall system."""
20
+ HEALTHY = "healthy"
21
+ DEGRADED = "degraded"
22
+ UNHEALTHY = "unhealthy"
23
+
24
+
25
+ @dataclass(frozen=True)
26
+ class ComponentHealth:
27
+ """Health result for a single component."""
28
+ name: str
29
+ status: HealthStatus
30
+ message: str = ""
31
+ latency_ms: float = 0.0
32
+
33
+
34
+ @dataclass
35
+ class HealthReport:
36
+ """Aggregate health report for all registered components."""
37
+ status: HealthStatus
38
+ components: dict[str, ComponentHealth]
39
+ timestamp: str
40
+ version: str
41
+ uptime_seconds: float
42
+
43
+ def to_dict(self) -> dict:
44
+ """Return a JSON-serializable dictionary."""
45
+ return {
46
+ "status": self.status.value,
47
+ "components": {
48
+ name: {
49
+ "name": comp.name,
50
+ "status": comp.status.value,
51
+ "message": comp.message,
52
+ "latency_ms": comp.latency_ms,
53
+ }
54
+ for name, comp in self.components.items()
55
+ },
56
+ "timestamp": self.timestamp,
57
+ "version": self.version,
58
+ "uptime_seconds": self.uptime_seconds,
59
+ }
60
+
61
+ def is_healthy(self) -> bool:
62
+ """True when aggregate status is HEALTHY."""
63
+ return self.status == HealthStatus.HEALTHY
64
+
65
+ def is_ready(self) -> bool:
66
+ """True when the system is ready to serve (not UNHEALTHY)."""
67
+ return self.status != HealthStatus.UNHEALTHY
68
+
69
+
70
+ class HealthChecker:
71
+ """Thread-safe health checker with pluggable component checks.
72
+
73
+ Args:
74
+ version: Application version string included in reports.
75
+ """
76
+
77
+ def __init__(self, version: str = "1.0.0") -> None:
78
+ self._checks: dict[str, Callable[[], ComponentHealth]] = {}
79
+ self._start_time = datetime.now(timezone.utc)
80
+ self._version = version
81
+ self._lock = threading.Lock()
82
+
83
+ # -- registration ------------------------------------------------------
84
+
85
+ def register_check(
86
+ self, name: str, check_fn: Callable[[], ComponentHealth]
87
+ ) -> None:
88
+ """Register a named health check function (thread-safe)."""
89
+ with self._lock:
90
+ self._checks[name] = check_fn
91
+
92
+ # -- probes ------------------------------------------------------------
93
+
94
+ def check_health(self) -> HealthReport:
95
+ """Run **all** registered checks and return a full report."""
96
+ with self._lock:
97
+ checks = dict(self._checks)
98
+
99
+ components: dict[str, ComponentHealth] = {}
100
+ for name, fn in checks.items():
101
+ start = time.monotonic()
102
+ try:
103
+ result = fn()
104
+ except Exception as exc:
105
+ result = ComponentHealth(
106
+ name=name,
107
+ status=HealthStatus.UNHEALTHY,
108
+ message=str(exc),
109
+ )
110
+ elapsed_ms = (time.monotonic() - start) * 1000.0
111
+ # Preserve check-reported latency if non-zero, else use measured.
112
+ latency = result.latency_ms if result.latency_ms else elapsed_ms
113
+ components[name] = ComponentHealth(
114
+ name=result.name,
115
+ status=result.status,
116
+ message=result.message,
117
+ latency_ms=latency,
118
+ )
119
+
120
+ return self._build_report(components)
121
+
122
+ def check_ready(self) -> HealthReport:
123
+ """Readiness probe — same as full health check."""
124
+ return self.check_health()
125
+
126
+ def check_live(self) -> HealthReport:
127
+ """Liveness probe — lightweight; returns HEALTHY if the process is up."""
128
+ components: dict[str, ComponentHealth] = {
129
+ "process": ComponentHealth(
130
+ name="process",
131
+ status=HealthStatus.HEALTHY,
132
+ message="alive",
133
+ )
134
+ }
135
+ return self._build_report(components)
136
+
137
+ # -- built-in checks ---------------------------------------------------
138
+
139
+ def _check_policy_engine(self) -> ComponentHealth:
140
+ """Built-in check that validates the policy engine can create a policy."""
141
+ from .base import GovernancePolicy
142
+
143
+ start = time.monotonic()
144
+ try:
145
+ GovernancePolicy(name="health-probe")
146
+ elapsed = (time.monotonic() - start) * 1000.0
147
+ return ComponentHealth(
148
+ name="policy_engine",
149
+ status=HealthStatus.HEALTHY,
150
+ message="policy engine operational",
151
+ latency_ms=elapsed,
152
+ )
153
+ except Exception as exc:
154
+ elapsed = (time.monotonic() - start) * 1000.0
155
+ return ComponentHealth(
156
+ name="policy_engine",
157
+ status=HealthStatus.UNHEALTHY,
158
+ message=str(exc),
159
+ latency_ms=elapsed,
160
+ )
161
+
162
+ def _check_audit_backend(self) -> ComponentHealth:
163
+ """Built-in check stub for the audit backend."""
164
+ return ComponentHealth(
165
+ name="audit_backend",
166
+ status=HealthStatus.HEALTHY,
167
+ message="audit backend reachable",
168
+ )
169
+
170
+ # -- helpers -----------------------------------------------------------
171
+
172
+ def _build_report(
173
+ self, components: dict[str, ComponentHealth]
174
+ ) -> HealthReport:
175
+ status = self._aggregate_status(components)
176
+ now = datetime.now(timezone.utc)
177
+ uptime = (now - self._start_time).total_seconds()
178
+ return HealthReport(
179
+ status=status,
180
+ components=components,
181
+ timestamp=now.isoformat() + "Z",
182
+ version=self._version,
183
+ uptime_seconds=uptime,
184
+ )
185
+
186
+ @staticmethod
187
+ def _aggregate_status(
188
+ components: dict[str, ComponentHealth],
189
+ ) -> HealthStatus:
190
+ if not components:
191
+ return HealthStatus.HEALTHY
192
+ statuses = {c.status for c in components.values()}
193
+ if HealthStatus.UNHEALTHY in statuses:
194
+ return HealthStatus.UNHEALTHY
195
+ if HealthStatus.DEGRADED in statuses:
196
+ return HealthStatus.DEGRADED
197
+ return HealthStatus.HEALTHY