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,239 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """
4
+ Standalone policy evaluator for Agent-OS governance.
5
+
6
+ Evaluates declarative PolicyDocuments against an execution context dict,
7
+ returning a PolicyDecision with matched rule, action, and audit information.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import logging
13
+ import re
14
+ from datetime import datetime, timezone
15
+ from pathlib import Path
16
+ from typing import Any
17
+
18
+ from pydantic import BaseModel, Field
19
+
20
+ from .schema import PolicyAction, PolicyDocument, PolicyOperator, PolicyRule
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ class PolicyDecision(BaseModel):
26
+ """Result of evaluating policies against an execution context."""
27
+
28
+ allowed: bool = True
29
+ matched_rule: str | None = None
30
+ action: str = "allow"
31
+ reason: str = "No rules matched; default action applied"
32
+ audit_entry: dict[str, Any] = Field(default_factory=dict)
33
+
34
+
35
+ class PolicyEvaluator:
36
+ """Evaluates a set of PolicyDocuments against execution contexts.
37
+
38
+ Supports external policy backends (OPA/Rego, Cedar) alongside the
39
+ native YAML/JSON engine. YAML rules are evaluated first; if no YAML
40
+ rule matches, external backends are consulted in registration order.
41
+ """
42
+
43
+ def __init__(self, policies: list[PolicyDocument] | None = None) -> None:
44
+ self.policies: list[PolicyDocument] = policies or []
45
+ self._backends: list[Any] = []
46
+
47
+ def load_policies(self, directory: str | Path) -> None:
48
+ """Load all YAML policy files from a directory."""
49
+ directory = Path(directory)
50
+ for path in sorted(directory.glob("*.yaml")):
51
+ self.policies.append(PolicyDocument.from_yaml(path))
52
+ for path in sorted(directory.glob("*.yml")):
53
+ self.policies.append(PolicyDocument.from_yaml(path))
54
+
55
+ def add_backend(self, backend: Any) -> None:
56
+ """Register an external policy backend (OPA, Cedar, etc.).
57
+
58
+ Backends are consulted in registration order when no YAML rule
59
+ matches. Each backend must implement ``evaluate(context) ->
60
+ BackendDecision`` and a ``name`` property.
61
+
62
+ Args:
63
+ backend: An ``ExternalPolicyBackend`` implementation such as
64
+ ``OPABackend`` or ``CedarBackend`` from
65
+ ``agent_os.policies.backends``.
66
+ """
67
+ self._backends.append(backend)
68
+
69
+ def load_rego(
70
+ self,
71
+ rego_path: str | None = None,
72
+ rego_content: str | None = None,
73
+ package: str = "agentos",
74
+ ) -> Any:
75
+ """Convenience: register an OPA/Rego backend.
76
+
77
+ Args:
78
+ rego_path: Path to a ``.rego`` file.
79
+ rego_content: Inline Rego policy string.
80
+ package: Rego package name for query construction.
81
+
82
+ Returns:
83
+ The ``OPABackend`` instance.
84
+ """
85
+ from .backends import OPABackend
86
+
87
+ backend = OPABackend(
88
+ rego_path=rego_path, rego_content=rego_content, package=package
89
+ )
90
+ self.add_backend(backend)
91
+ return backend
92
+
93
+ def load_cedar(
94
+ self,
95
+ policy_path: str | None = None,
96
+ policy_content: str | None = None,
97
+ entities: list[dict[str, Any]] | None = None,
98
+ ) -> Any:
99
+ """Convenience: register a Cedar backend.
100
+
101
+ Args:
102
+ policy_path: Path to a ``.cedar`` policy file.
103
+ policy_content: Inline Cedar policy string.
104
+ entities: Cedar entities for authorization context.
105
+
106
+ Returns:
107
+ The ``CedarBackend`` instance.
108
+ """
109
+ from .backends import CedarBackend
110
+
111
+ backend = CedarBackend(
112
+ policy_path=policy_path,
113
+ policy_content=policy_content,
114
+ entities=entities,
115
+ )
116
+ self.add_backend(backend)
117
+ return backend
118
+
119
+ def evaluate(self, context: dict[str, Any]) -> PolicyDecision:
120
+ """Evaluate all loaded policy rules against the given context.
121
+
122
+ Rules are sorted by priority (descending). The first matching rule
123
+ determines the decision. If no YAML rule matches and external
124
+ backends are registered, they are consulted in order. If nothing
125
+ matches, the default action from the first policy (or global allow)
126
+ is used.
127
+ """
128
+ try:
129
+ all_rules: list[tuple[PolicyRule, PolicyDocument]] = []
130
+ for doc in self.policies:
131
+ for rule in doc.rules:
132
+ all_rules.append((rule, doc))
133
+
134
+ # Sort by priority descending so highest priority is checked first
135
+ all_rules.sort(key=lambda pair: pair[0].priority, reverse=True)
136
+
137
+ for rule, doc in all_rules:
138
+ if _match_condition(rule.condition, context):
139
+ allowed = rule.action in (PolicyAction.ALLOW, PolicyAction.AUDIT)
140
+ return PolicyDecision(
141
+ allowed=allowed,
142
+ matched_rule=rule.name,
143
+ action=rule.action.value,
144
+ reason=rule.message or f"Matched rule '{rule.name}'",
145
+ audit_entry={
146
+ "policy": doc.name,
147
+ "rule": rule.name,
148
+ "action": rule.action.value,
149
+ "context_snapshot": context,
150
+ "timestamp": datetime.now(timezone.utc).isoformat(),
151
+ },
152
+ )
153
+
154
+ # No YAML rule matched — consult external backends
155
+ for backend in self._backends:
156
+ result = backend.evaluate(context)
157
+ if result.error is None:
158
+ return PolicyDecision(
159
+ allowed=result.allowed,
160
+ matched_rule=None,
161
+ action=result.action,
162
+ reason=result.reason,
163
+ audit_entry={
164
+ "policy": f"external:{backend.name}",
165
+ "rule": None,
166
+ "action": result.action,
167
+ "backend": backend.name,
168
+ "evaluation_ms": result.evaluation_ms,
169
+ "context_snapshot": context,
170
+ "timestamp": datetime.now(timezone.utc).isoformat(),
171
+ },
172
+ )
173
+
174
+ # No rule matched — apply defaults
175
+ default_action = PolicyAction.ALLOW
176
+ if self.policies:
177
+ default_action = self.policies[0].defaults.action
178
+ allowed = default_action in (PolicyAction.ALLOW, PolicyAction.AUDIT)
179
+ return PolicyDecision(
180
+ allowed=allowed,
181
+ action=default_action.value,
182
+ reason="No rules matched; default action applied",
183
+ audit_entry={
184
+ "policy": self.policies[0].name if self.policies else None,
185
+ "rule": None,
186
+ "action": default_action.value,
187
+ "context_snapshot": context,
188
+ "timestamp": datetime.now(timezone.utc).isoformat(),
189
+ },
190
+ )
191
+ except Exception:
192
+ logger.error(
193
+ "Policy evaluation error — denying access (fail closed)",
194
+ exc_info=True,
195
+ )
196
+ return PolicyDecision(
197
+ allowed=False,
198
+ action="deny",
199
+ reason="Policy evaluation error — access denied (fail closed)",
200
+ audit_entry={
201
+ "policy": None,
202
+ "rule": None,
203
+ "action": "deny",
204
+ "context_snapshot": context,
205
+ "timestamp": datetime.now(timezone.utc).isoformat(),
206
+ "error": True,
207
+ },
208
+ )
209
+
210
+
211
+ def _match_condition(condition: Any, context: dict[str, Any]) -> bool:
212
+ """Check whether a single PolicyCondition matches the context."""
213
+ ctx_value = context.get(condition.field)
214
+ if ctx_value is None:
215
+ return False
216
+
217
+ op = condition.operator
218
+ target = condition.value
219
+
220
+ if op == PolicyOperator.EQ:
221
+ return ctx_value == target
222
+ if op == PolicyOperator.NE:
223
+ return ctx_value != target
224
+ if op == PolicyOperator.GT:
225
+ return ctx_value > target
226
+ if op == PolicyOperator.LT:
227
+ return ctx_value < target
228
+ if op == PolicyOperator.GTE:
229
+ return ctx_value >= target
230
+ if op == PolicyOperator.LTE:
231
+ return ctx_value <= target
232
+ if op == PolicyOperator.IN:
233
+ return ctx_value in target
234
+ if op == PolicyOperator.CONTAINS:
235
+ return target in ctx_value
236
+ if op == PolicyOperator.MATCHES:
237
+ return bool(re.search(str(target), str(ctx_value)))
238
+
239
+ return False
@@ -0,0 +1,228 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://github.com/microsoft/agent-governance-toolkit/policy-schema/v1.0",
4
+ "title": "Agent-OS Policy Document",
5
+ "description": "JSON Schema for Agent-OS declarative governance policy documents.",
6
+ "type": "object",
7
+ "properties": {
8
+ "version": {
9
+ "type": "string",
10
+ "description": "Schema version identifier.",
11
+ "default": "1.0"
12
+ },
13
+ "name": {
14
+ "type": "string",
15
+ "description": "Human-readable policy name.",
16
+ "default": "unnamed"
17
+ },
18
+ "description": {
19
+ "type": "string",
20
+ "description": "Detailed description of the policy purpose.",
21
+ "default": ""
22
+ },
23
+ "rules": {
24
+ "type": "array",
25
+ "description": "Ordered list of governance rules.",
26
+ "items": {
27
+ "$ref": "#/definitions/PolicyRule"
28
+ },
29
+ "default": []
30
+ },
31
+ "defaults": {
32
+ "$ref": "#/definitions/PolicyDefaults",
33
+ "description": "Default settings applied when no rule matches."
34
+ },
35
+ "a2a_conversation_policy": {
36
+ "$ref": "#/definitions/A2AConversationPolicy",
37
+ "description": "Policy for A2A conversation monitoring and feedback loop prevention."
38
+ }
39
+ },
40
+ "additionalProperties": false,
41
+ "definitions": {
42
+ "PolicyOperator": {
43
+ "type": "string",
44
+ "description": "Comparison operator for policy conditions.",
45
+ "enum": ["eq", "ne", "gt", "lt", "gte", "lte", "in", "matches", "contains"]
46
+ },
47
+ "PolicyAction": {
48
+ "type": "string",
49
+ "description": "Action a policy rule prescribes.",
50
+ "enum": ["allow", "deny", "audit", "block"]
51
+ },
52
+ "PolicyCondition": {
53
+ "type": "object",
54
+ "description": "A single condition evaluated against execution context.",
55
+ "properties": {
56
+ "field": {
57
+ "type": "string",
58
+ "description": "Context field name, e.g. 'tool_name', 'token_count'."
59
+ },
60
+ "operator": {
61
+ "$ref": "#/definitions/PolicyOperator",
62
+ "description": "Comparison operator."
63
+ },
64
+ "value": {
65
+ "description": "Value to compare against. Type depends on the operator."
66
+ }
67
+ },
68
+ "required": ["field", "operator", "value"],
69
+ "additionalProperties": false
70
+ },
71
+ "PolicyRule": {
72
+ "type": "object",
73
+ "description": "A single governance rule within a policy document.",
74
+ "properties": {
75
+ "name": {
76
+ "type": "string",
77
+ "description": "Unique rule identifier."
78
+ },
79
+ "condition": {
80
+ "$ref": "#/definitions/PolicyCondition",
81
+ "description": "Condition that triggers this rule."
82
+ },
83
+ "action": {
84
+ "$ref": "#/definitions/PolicyAction",
85
+ "description": "Action to take when the condition matches."
86
+ },
87
+ "priority": {
88
+ "type": "integer",
89
+ "description": "Higher priority rules are evaluated first.",
90
+ "default": 0
91
+ },
92
+ "message": {
93
+ "type": "string",
94
+ "description": "Human-readable explanation of the rule.",
95
+ "default": ""
96
+ }
97
+ },
98
+ "required": ["name", "condition", "action"],
99
+ "additionalProperties": false
100
+ },
101
+ "PolicyDefaults": {
102
+ "type": "object",
103
+ "description": "Default settings applied when no rule matches.",
104
+ "properties": {
105
+ "action": {
106
+ "$ref": "#/definitions/PolicyAction",
107
+ "description": "Default action when no rule matches.",
108
+ "default": "allow"
109
+ },
110
+ "max_tokens": {
111
+ "type": "integer",
112
+ "description": "Maximum token budget.",
113
+ "default": 4096
114
+ },
115
+ "max_tool_calls": {
116
+ "type": "integer",
117
+ "description": "Maximum number of tool calls allowed.",
118
+ "default": 10
119
+ },
120
+ "confidence_threshold": {
121
+ "type": "number",
122
+ "description": "Minimum confidence score required.",
123
+ "default": 0.8,
124
+ "minimum": 0.0,
125
+ "maximum": 1.0
126
+ }
127
+ },
128
+ "additionalProperties": false
129
+ },
130
+ "A2AConversationPolicy": {
131
+ "type": "object",
132
+ "description": "Policy for monitoring agent-to-agent conversations (OWASP ASI-8/10).",
133
+ "properties": {
134
+ "enabled": {
135
+ "type": "boolean",
136
+ "description": "Enable conversation guardian on A2A messages.",
137
+ "default": true
138
+ },
139
+ "escalation_score_threshold": {
140
+ "type": "number",
141
+ "description": "Escalation score that triggers PAUSE action.",
142
+ "default": 0.6,
143
+ "minimum": 0.0,
144
+ "maximum": 1.0
145
+ },
146
+ "escalation_warn_threshold": {
147
+ "type": "number",
148
+ "description": "Escalation score that triggers WARN action.",
149
+ "default": 0.4,
150
+ "minimum": 0.0,
151
+ "maximum": 1.0
152
+ },
153
+ "offensive_score_threshold": {
154
+ "type": "number",
155
+ "description": "Offensive intent score that triggers PAUSE action.",
156
+ "default": 0.5,
157
+ "minimum": 0.0,
158
+ "maximum": 1.0
159
+ },
160
+ "offensive_critical_threshold": {
161
+ "type": "number",
162
+ "description": "Offensive intent score that triggers QUARANTINE action.",
163
+ "default": 0.8,
164
+ "minimum": 0.0,
165
+ "maximum": 1.0
166
+ },
167
+ "max_retry_cycles": {
168
+ "type": "integer",
169
+ "description": "Max error-retry cycles before BREAK.",
170
+ "default": 3,
171
+ "minimum": 1
172
+ },
173
+ "max_conversation_turns": {
174
+ "type": "integer",
175
+ "description": "Max turns in a conversation before BREAK.",
176
+ "default": 30,
177
+ "minimum": 1
178
+ },
179
+ "on_escalation_detected": {
180
+ "type": "string",
181
+ "description": "Action when escalation is detected.",
182
+ "enum": ["warn", "pause", "break", "quarantine"],
183
+ "default": "warn"
184
+ },
185
+ "on_offensive_detected": {
186
+ "type": "string",
187
+ "description": "Action when offensive intent is detected.",
188
+ "enum": ["warn", "pause", "break", "quarantine"],
189
+ "default": "break"
190
+ },
191
+ "on_feedback_loop": {
192
+ "type": "string",
193
+ "description": "Action when feedback loop is detected.",
194
+ "enum": ["warn", "pause", "break", "quarantine"],
195
+ "default": "break"
196
+ },
197
+ "require_human_approval_on": {
198
+ "type": "array",
199
+ "description": "Conditions requiring human approval before proceeding.",
200
+ "items": {
201
+ "type": "string",
202
+ "enum": ["escalation_above_threshold", "offensive_intent_detected", "feedback_loop_detected"]
203
+ },
204
+ "default": []
205
+ },
206
+ "audit": {
207
+ "type": "object",
208
+ "description": "Audit settings for conversation monitoring.",
209
+ "properties": {
210
+ "capture_full_transcript": {
211
+ "type": "boolean",
212
+ "description": "Log full message content in audit trail.",
213
+ "default": true
214
+ },
215
+ "retention_days": {
216
+ "type": "integer",
217
+ "description": "Days to retain conversation audit logs. EU AI Act Art. 26(6) requires minimum 180 days for high-risk systems.",
218
+ "default": 180,
219
+ "minimum": 30
220
+ }
221
+ },
222
+ "additionalProperties": false
223
+ }
224
+ },
225
+ "additionalProperties": false
226
+ }
227
+ }
228
+ }
@@ -0,0 +1,145 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """Shared rate limiting primitives for toolkit policy layers.
4
+
5
+ These primitives provide a canonical token-bucket foundation without collapsing
6
+ layer-specific limiters that serve different architectural boundaries.
7
+
8
+ See also:
9
+ - hypervisor.security.rate_limiter: runtime-layer per-agent/per-ring limits.
10
+ - agent_os.integrations.rate_limiter: tool-call policy limits in Agent OS.
11
+ - agentmesh.services.rate_limiter: service/proxy-level limits in Agent Mesh.
12
+ - agentmesh.services.rate_limit_middleware: HTTP edge middleware in Agent Mesh.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import threading
18
+ import time
19
+ from dataclasses import dataclass, field
20
+ from typing import Any
21
+
22
+
23
+ class RateLimitExceeded(Exception):
24
+ """Raised when a rate-limited operation cannot proceed."""
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class RateLimitConfig:
29
+ """Basic token-bucket configuration.
30
+
31
+ Args:
32
+ capacity: Maximum burst size (the most tokens the bucket may hold).
33
+ refill_rate: Tokens replenished per second.
34
+ initial_tokens: Optional initial token count. Defaults to ``capacity``.
35
+ """
36
+
37
+ capacity: float
38
+ refill_rate: float
39
+ initial_tokens: float | None = None
40
+
41
+ def __post_init__(self) -> None:
42
+ if self.capacity <= 0:
43
+ raise ValueError("capacity must be positive")
44
+ if self.refill_rate < 0:
45
+ raise ValueError("refill_rate must be non-negative")
46
+ if self.initial_tokens is not None and not 0 <= self.initial_tokens <= self.capacity:
47
+ raise ValueError(
48
+ "initial_tokens must be between 0 and capacity"
49
+ )
50
+
51
+ @property
52
+ def rate(self) -> float:
53
+ """Alias for ``refill_rate`` for callers that prefer ``rate`` terminology."""
54
+ return self.refill_rate
55
+
56
+
57
+ @dataclass
58
+ class TokenBucket:
59
+ """Thread-safe token bucket for rate limiting."""
60
+
61
+ capacity: float
62
+ tokens: float
63
+ refill_rate: float
64
+ last_refill: float = field(default_factory=time.monotonic)
65
+ _lock: Any = field(
66
+ default_factory=threading.Lock,
67
+ init=False,
68
+ repr=False,
69
+ compare=False,
70
+ )
71
+
72
+ def __post_init__(self) -> None:
73
+ if self.capacity <= 0:
74
+ raise ValueError("capacity must be positive")
75
+ if self.refill_rate < 0:
76
+ raise ValueError("refill_rate must be non-negative")
77
+ if not 0 <= self.tokens <= self.capacity:
78
+ raise ValueError("tokens must be between 0 and capacity")
79
+
80
+ @classmethod
81
+ def from_config(cls, config: RateLimitConfig) -> "TokenBucket":
82
+ """Build a token bucket from a :class:`RateLimitConfig`."""
83
+ initial_tokens = (
84
+ config.capacity if config.initial_tokens is None else config.initial_tokens
85
+ )
86
+ return cls(
87
+ capacity=config.capacity,
88
+ tokens=initial_tokens,
89
+ refill_rate=config.refill_rate,
90
+ )
91
+
92
+ def _refill_unlocked(self, now: float | None = None) -> None:
93
+ current = time.monotonic() if now is None else now
94
+ elapsed = current - self.last_refill
95
+ if elapsed <= 0:
96
+ return
97
+ self.tokens = min(self.capacity, self.tokens + elapsed * self.refill_rate)
98
+ self.last_refill = current
99
+
100
+ def consume(self, tokens: float = 1.0) -> bool:
101
+ """Try to consume *tokens*. Returns ``True`` when enough tokens exist."""
102
+ if tokens <= 0:
103
+ raise ValueError("tokens must be positive")
104
+ with self._lock:
105
+ self._refill_unlocked()
106
+ if self.tokens >= tokens:
107
+ self.tokens -= tokens
108
+ return True
109
+ return False
110
+
111
+ @property
112
+ def available(self) -> float:
113
+ """Current token count after refilling for elapsed time."""
114
+ with self._lock:
115
+ self._refill_unlocked()
116
+ return self.tokens
117
+
118
+ def tokens_available(self) -> float:
119
+ """Method alias for callers that prefer a function over a property."""
120
+ return self.available
121
+
122
+ def time_until_available(self, tokens: float = 1.0) -> float:
123
+ """Return seconds until *tokens* are available."""
124
+ if tokens <= 0:
125
+ raise ValueError("tokens must be positive")
126
+ with self._lock:
127
+ self._refill_unlocked()
128
+ if self.tokens >= tokens:
129
+ return 0.0
130
+ if self.refill_rate == 0:
131
+ return float("inf")
132
+ deficit = tokens - self.tokens
133
+ return deficit / self.refill_rate
134
+
135
+ def reset(self, tokens: float | None = None) -> None:
136
+ """Reset the bucket to *tokens* or back to full capacity."""
137
+ target_tokens = self.capacity if tokens is None else tokens
138
+ if not 0 <= target_tokens <= self.capacity:
139
+ raise ValueError("tokens must be between 0 and capacity")
140
+ with self._lock:
141
+ self.tokens = target_tokens
142
+ self.last_refill = time.monotonic()
143
+
144
+
145
+ __all__ = ["RateLimitConfig", "RateLimitExceeded", "TokenBucket"]