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.
- agent_control_plane/__init__.py +662 -0
- agent_control_plane/a2a_adapter.py +543 -0
- agent_control_plane/adapter.py +417 -0
- agent_control_plane/agent_hibernation.py +394 -0
- agent_control_plane/agent_kernel.py +470 -0
- agent_control_plane/compliance.py +720 -0
- agent_control_plane/constraint_graphs.py +478 -0
- agent_control_plane/control_plane.py +854 -0
- agent_control_plane/example_executors.py +195 -0
- agent_control_plane/execution_engine.py +231 -0
- agent_control_plane/flight_recorder.py +846 -0
- agent_control_plane/governance_layer.py +435 -0
- agent_control_plane/hf_utils.py +563 -0
- agent_control_plane/interfaces/__init__.py +55 -0
- agent_control_plane/interfaces/kernel_interface.py +361 -0
- agent_control_plane/interfaces/plugin_interface.py +497 -0
- agent_control_plane/interfaces/protocol_interfaces.py +387 -0
- agent_control_plane/kernel_space.py +1009 -0
- agent_control_plane/langchain_adapter.py +424 -0
- agent_control_plane/lifecycle.py +3113 -0
- agent_control_plane/mcp_adapter.py +653 -0
- agent_control_plane/ml_safety.py +563 -0
- agent_control_plane/multimodal.py +727 -0
- agent_control_plane/mute_agent.py +422 -0
- agent_control_plane/observability.py +787 -0
- agent_control_plane/orchestrator.py +482 -0
- agent_control_plane/plugin_registry.py +750 -0
- agent_control_plane/policy_engine.py +954 -0
- agent_control_plane/process_isolation.py +777 -0
- agent_control_plane/shadow_mode.py +310 -0
- agent_control_plane/signals.py +493 -0
- agent_control_plane/supervisor_agents.py +430 -0
- agent_control_plane/time_travel_debugger.py +557 -0
- agent_control_plane/tool_registry.py +452 -0
- agent_control_plane/vfs.py +697 -0
- agent_kernel/__init__.py +69 -0
- agent_kernel/analyzer.py +435 -0
- agent_kernel/auditor.py +36 -0
- agent_kernel/completeness_auditor.py +237 -0
- agent_kernel/detector.py +203 -0
- agent_kernel/kernel.py +744 -0
- agent_kernel/memory_manager.py +85 -0
- agent_kernel/models.py +374 -0
- agent_kernel/nudge_mechanism.py +263 -0
- agent_kernel/outcome_analyzer.py +338 -0
- agent_kernel/patcher.py +582 -0
- agent_kernel/semantic_analyzer.py +316 -0
- agent_kernel/semantic_purge.py +349 -0
- agent_kernel/simulator.py +449 -0
- agent_kernel/teacher.py +85 -0
- agent_kernel/triage.py +152 -0
- agent_os/__init__.py +409 -0
- agent_os/_adversarial_impl.py +200 -0
- agent_os/_circuit_breaker_impl.py +232 -0
- agent_os/_mcp_metrics.py +193 -0
- agent_os/adversarial.py +20 -0
- agent_os/agents_compat.py +490 -0
- agent_os/audit_logger.py +135 -0
- agent_os/base_agent.py +651 -0
- agent_os/circuit_breaker.py +34 -0
- agent_os/cli/__init__.py +659 -0
- agent_os/cli/cmd_audit.py +128 -0
- agent_os/cli/cmd_init.py +152 -0
- agent_os/cli/cmd_policy.py +41 -0
- agent_os/cli/cmd_policy_gen.py +180 -0
- agent_os/cli/cmd_validate.py +258 -0
- agent_os/cli/mcp_scan.py +265 -0
- agent_os/cli/output.py +192 -0
- agent_os/cli/policy_checker.py +330 -0
- agent_os/compat.py +74 -0
- agent_os/constraint_graph.py +234 -0
- agent_os/content_governance.py +140 -0
- agent_os/context_budget.py +305 -0
- agent_os/credential_redactor.py +224 -0
- agent_os/diff_policy.py +89 -0
- agent_os/egress_policy.py +159 -0
- agent_os/escalation.py +276 -0
- agent_os/event_bus.py +124 -0
- agent_os/exceptions.py +180 -0
- agent_os/execution_context_policy.py +141 -0
- agent_os/github_enterprise.py +96 -0
- agent_os/health.py +20 -0
- agent_os/integrations/__init__.py +279 -0
- agent_os/integrations/a2a_adapter.py +279 -0
- agent_os/integrations/agent_lightning/__init__.py +30 -0
- agent_os/integrations/anthropic_adapter.py +420 -0
- agent_os/integrations/autogen_adapter.py +620 -0
- agent_os/integrations/base.py +1137 -0
- agent_os/integrations/compat.py +229 -0
- agent_os/integrations/config.py +98 -0
- agent_os/integrations/conversation_guardian.py +957 -0
- agent_os/integrations/crewai_adapter.py +467 -0
- agent_os/integrations/drift_detector.py +425 -0
- agent_os/integrations/dry_run.py +124 -0
- agent_os/integrations/escalation.py +582 -0
- agent_os/integrations/gemini_adapter.py +364 -0
- agent_os/integrations/google_adk_adapter.py +633 -0
- agent_os/integrations/guardrails_adapter.py +394 -0
- agent_os/integrations/health.py +197 -0
- agent_os/integrations/langchain_adapter.py +654 -0
- agent_os/integrations/llamafirewall.py +343 -0
- agent_os/integrations/llamaindex_adapter.py +188 -0
- agent_os/integrations/logging.py +191 -0
- agent_os/integrations/maf_adapter.py +631 -0
- agent_os/integrations/mistral_adapter.py +365 -0
- agent_os/integrations/openai_adapter.py +816 -0
- agent_os/integrations/openai_agents_sdk.py +406 -0
- agent_os/integrations/policy_compose.py +171 -0
- agent_os/integrations/profiling.py +144 -0
- agent_os/integrations/pydantic_ai_adapter.py +420 -0
- agent_os/integrations/rate_limiter.py +130 -0
- agent_os/integrations/rbac.py +143 -0
- agent_os/integrations/registry.py +113 -0
- agent_os/integrations/scope_guard.py +303 -0
- agent_os/integrations/semantic_kernel_adapter.py +769 -0
- agent_os/integrations/smolagents_adapter.py +629 -0
- agent_os/integrations/templates.py +178 -0
- agent_os/integrations/token_budget.py +134 -0
- agent_os/integrations/tool_aliases.py +190 -0
- agent_os/integrations/webhooks.py +177 -0
- agent_os/lite.py +208 -0
- agent_os/mcp_gateway.py +385 -0
- agent_os/mcp_message_signer.py +273 -0
- agent_os/mcp_protocols.py +161 -0
- agent_os/mcp_response_scanner.py +232 -0
- agent_os/mcp_security.py +924 -0
- agent_os/mcp_session_auth.py +231 -0
- agent_os/mcp_sliding_rate_limiter.py +184 -0
- agent_os/memory_guard.py +409 -0
- agent_os/metrics.py +134 -0
- agent_os/mute.py +428 -0
- agent_os/mute_agent.py +209 -0
- agent_os/policies/__init__.py +77 -0
- agent_os/policies/async_evaluator.py +275 -0
- agent_os/policies/backends.py +670 -0
- agent_os/policies/bridge.py +169 -0
- agent_os/policies/budget.py +85 -0
- agent_os/policies/cli.py +294 -0
- agent_os/policies/conflict_resolution.py +270 -0
- agent_os/policies/data_classification.py +252 -0
- agent_os/policies/evaluator.py +239 -0
- agent_os/policies/policy_schema.json +228 -0
- agent_os/policies/rate_limiting.py +145 -0
- agent_os/policies/schema.py +115 -0
- agent_os/policies/shared.py +331 -0
- agent_os/prompt_injection.py +694 -0
- agent_os/providers.py +182 -0
- agent_os/py.typed +0 -0
- agent_os/retry.py +81 -0
- agent_os/reversibility.py +251 -0
- agent_os/sandbox.py +432 -0
- agent_os/sandbox_provider.py +140 -0
- agent_os/secure_codegen.py +525 -0
- agent_os/security_skills.py +538 -0
- agent_os/semantic_policy.py +422 -0
- agent_os/server/__init__.py +15 -0
- agent_os/server/__main__.py +25 -0
- agent_os/server/app.py +277 -0
- agent_os/server/models.py +104 -0
- agent_os/shift_left_metrics.py +130 -0
- agent_os/stateless.py +742 -0
- agent_os/supervisor.py +148 -0
- agent_os/task_outcome.py +148 -0
- agent_os/transparency.py +181 -0
- agent_os/trust_root.py +128 -0
- agent_os_kernel-3.1.0.dist-info/METADATA +1269 -0
- agent_os_kernel-3.1.0.dist-info/RECORD +337 -0
- agent_os_kernel-3.1.0.dist-info/WHEEL +4 -0
- agent_os_kernel-3.1.0.dist-info/entry_points.txt +2 -0
- agent_os_kernel-3.1.0.dist-info/licenses/LICENSE +21 -0
- agent_os_observability/__init__.py +27 -0
- agent_os_observability/dashboards.py +898 -0
- agent_os_observability/metrics.py +398 -0
- agent_os_observability/server.py +223 -0
- agent_os_observability/tracer.py +232 -0
- agent_primitives/__init__.py +24 -0
- agent_primitives/failures.py +84 -0
- agent_primitives/py.typed +0 -0
- amb_core/__init__.py +177 -0
- amb_core/adapters/__init__.py +57 -0
- amb_core/adapters/aws_sqs_broker.py +376 -0
- amb_core/adapters/azure_servicebus_broker.py +340 -0
- amb_core/adapters/kafka_broker.py +260 -0
- amb_core/adapters/nats_broker.py +285 -0
- amb_core/adapters/rabbitmq_broker.py +235 -0
- amb_core/adapters/redis_broker.py +262 -0
- amb_core/broker.py +145 -0
- amb_core/bus.py +481 -0
- amb_core/cloudevents.py +509 -0
- amb_core/dlq.py +345 -0
- amb_core/hf_utils.py +536 -0
- amb_core/memory_broker.py +410 -0
- amb_core/models.py +141 -0
- amb_core/persistence.py +529 -0
- amb_core/schema.py +294 -0
- amb_core/tracing.py +358 -0
- atr/__init__.py +640 -0
- atr/access.py +348 -0
- atr/composition.py +645 -0
- atr/decorator.py +357 -0
- atr/executor.py +384 -0
- atr/health.py +557 -0
- atr/hf_utils.py +449 -0
- atr/injection.py +422 -0
- atr/metrics.py +440 -0
- atr/policies.py +403 -0
- atr/py.typed +2 -0
- atr/registry.py +452 -0
- atr/schema.py +480 -0
- atr/tools/safe/__init__.py +75 -0
- atr/tools/safe/calculator.py +467 -0
- atr/tools/safe/datetime_tool.py +443 -0
- atr/tools/safe/file_reader.py +402 -0
- atr/tools/safe/http_client.py +316 -0
- atr/tools/safe/json_parser.py +374 -0
- atr/tools/safe/text_tool.py +537 -0
- atr/tools/safe/toolkit.py +175 -0
- caas/__init__.py +162 -0
- caas/api/__init__.py +7 -0
- caas/api/server.py +1328 -0
- caas/caching.py +834 -0
- caas/cli.py +210 -0
- caas/conversation.py +223 -0
- caas/decay.py +72 -0
- caas/detection/__init__.py +9 -0
- caas/detection/detector.py +238 -0
- caas/enrichment.py +130 -0
- caas/gateway/__init__.py +27 -0
- caas/gateway/trust_gateway.py +474 -0
- caas/hf_utils.py +479 -0
- caas/ingestion/__init__.py +23 -0
- caas/ingestion/processors.py +253 -0
- caas/ingestion/structure_parser.py +188 -0
- caas/models.py +356 -0
- caas/pragmatic_truth.py +444 -0
- caas/routing/__init__.py +10 -0
- caas/routing/heuristic_router.py +58 -0
- caas/storage/__init__.py +9 -0
- caas/storage/store.py +389 -0
- caas/triad.py +213 -0
- caas/tuning/__init__.py +9 -0
- caas/tuning/tuner.py +329 -0
- caas/vfs/__init__.py +14 -0
- caas/vfs/filesystem.py +452 -0
- cmvk/__init__.py +218 -0
- cmvk/audit.py +402 -0
- cmvk/benchmarks.py +478 -0
- cmvk/constitutional.py +904 -0
- cmvk/hf_utils.py +301 -0
- cmvk/metrics.py +473 -0
- cmvk/profiles.py +300 -0
- cmvk/py.typed +0 -0
- cmvk/types.py +12 -0
- cmvk/verification.py +956 -0
- emk/__init__.py +89 -0
- emk/causal.py +352 -0
- emk/hf_utils.py +421 -0
- emk/indexer.py +83 -0
- emk/py.typed +0 -0
- emk/schema.py +204 -0
- emk/sleep_cycle.py +347 -0
- emk/store.py +281 -0
- iatp/__init__.py +166 -0
- iatp/attestation.py +461 -0
- iatp/cli.py +317 -0
- iatp/hf_utils.py +472 -0
- iatp/ipc_pipes.py +580 -0
- iatp/main.py +412 -0
- iatp/models/__init__.py +447 -0
- iatp/policy_engine.py +337 -0
- iatp/py.typed +2 -0
- iatp/recovery.py +321 -0
- iatp/security/__init__.py +270 -0
- iatp/sidecar/__init__.py +519 -0
- iatp/telemetry/__init__.py +164 -0
- iatp/tests/__init__.py +1 -0
- iatp/tests/test_attestation.py +370 -0
- iatp/tests/test_cli.py +131 -0
- iatp/tests/test_ed25519_attestation.py +211 -0
- iatp/tests/test_models.py +130 -0
- iatp/tests/test_policy_engine.py +347 -0
- iatp/tests/test_recovery.py +281 -0
- iatp/tests/test_security.py +222 -0
- iatp/tests/test_sidecar.py +167 -0
- iatp/tests/test_telemetry.py +175 -0
- mcp_kernel_server/__init__.py +28 -0
- mcp_kernel_server/cli.py +274 -0
- mcp_kernel_server/resources.py +217 -0
- mcp_kernel_server/server.py +564 -0
- mcp_kernel_server/tools.py +1174 -0
- mute_agent/__init__.py +68 -0
- mute_agent/core/__init__.py +1 -0
- mute_agent/core/execution_agent.py +166 -0
- mute_agent/core/handshake_protocol.py +201 -0
- mute_agent/core/reasoning_agent.py +238 -0
- mute_agent/knowledge_graph/__init__.py +1 -0
- mute_agent/knowledge_graph/graph_elements.py +65 -0
- mute_agent/knowledge_graph/multidimensional_graph.py +170 -0
- mute_agent/knowledge_graph/subgraph.py +224 -0
- mute_agent/listener/__init__.py +43 -0
- mute_agent/listener/adapters/__init__.py +31 -0
- mute_agent/listener/adapters/base_adapter.py +189 -0
- mute_agent/listener/adapters/caas_adapter.py +344 -0
- mute_agent/listener/adapters/control_plane_adapter.py +436 -0
- mute_agent/listener/adapters/iatp_adapter.py +332 -0
- mute_agent/listener/adapters/scak_adapter.py +251 -0
- mute_agent/listener/listener.py +610 -0
- mute_agent/listener/state_observer.py +436 -0
- mute_agent/listener/threshold_config.py +313 -0
- mute_agent/super_system/__init__.py +1 -0
- mute_agent/super_system/router.py +204 -0
- mute_agent/visualization/__init__.py +10 -0
- mute_agent/visualization/graph_debugger.py +502 -0
- nexus/README.md +60 -0
- nexus/__init__.py +51 -0
- nexus/arbiter.py +359 -0
- nexus/client.py +466 -0
- nexus/dmz.py +444 -0
- nexus/escrow.py +430 -0
- nexus/exceptions.py +286 -0
- nexus/pyproject.toml +36 -0
- nexus/registry.py +393 -0
- nexus/reputation.py +425 -0
- nexus/schemas/__init__.py +51 -0
- nexus/schemas/compliance.py +276 -0
- nexus/schemas/escrow.py +251 -0
- nexus/schemas/manifest.py +225 -0
- nexus/schemas/receipt.py +208 -0
- nexus/tests/__init__.py +0 -0
- nexus/tests/conftest.py +146 -0
- nexus/tests/test_arbiter.py +192 -0
- nexus/tests/test_dmz.py +194 -0
- nexus/tests/test_escrow.py +276 -0
- nexus/tests/test_exceptions.py +225 -0
- nexus/tests/test_registry.py +232 -0
- nexus/tests/test_reputation.py +328 -0
- nexus/tests/test_schemas.py +295 -0
|
@@ -0,0 +1,631 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""
|
|
4
|
+
Microsoft Agent Framework (MAF) Governance Adapter
|
|
5
|
+
|
|
6
|
+
Bridges the Agent OS governance toolkit into MAF's native middleware system.
|
|
7
|
+
Four composable middleware layers enforce policy, capability guards, audit
|
|
8
|
+
trails, and rogue-agent detection at every level of the agent stack:
|
|
9
|
+
|
|
10
|
+
- GovernancePolicyMiddleware (AgentMiddleware): Declarative policy enforcement
|
|
11
|
+
- CapabilityGuardMiddleware (FunctionMiddleware): Tool allow/deny lists
|
|
12
|
+
- AuditTrailMiddleware (AgentMiddleware): Tamper-proof audit logging
|
|
13
|
+
- RogueDetectionMiddleware (FunctionMiddleware): Behavioral anomaly detection
|
|
14
|
+
|
|
15
|
+
Each middleware works independently and can be composed in any combination.
|
|
16
|
+
|
|
17
|
+
Usage::
|
|
18
|
+
|
|
19
|
+
from agent_framework import Agent
|
|
20
|
+
from agent_os.integrations.maf_adapter import create_governance_middleware
|
|
21
|
+
|
|
22
|
+
middleware = create_governance_middleware(
|
|
23
|
+
policy_directory="policies/",
|
|
24
|
+
allowed_tools=["web_search", "file_read"],
|
|
25
|
+
enable_rogue_detection=True,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
agent = Agent(
|
|
29
|
+
name="researcher",
|
|
30
|
+
instructions="You are a research assistant.",
|
|
31
|
+
middleware=middleware,
|
|
32
|
+
)
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
from __future__ import annotations
|
|
36
|
+
|
|
37
|
+
import logging
|
|
38
|
+
import time
|
|
39
|
+
from pathlib import Path
|
|
40
|
+
from typing import Any, Awaitable, Callable
|
|
41
|
+
|
|
42
|
+
from agent_os.policies import PolicyDecision, PolicyEvaluator
|
|
43
|
+
from agentmesh.governance import AuditEntry, AuditLog
|
|
44
|
+
from agent_sre.anomaly import RiskLevel, RogueAgentDetector, RogueDetectorConfig
|
|
45
|
+
|
|
46
|
+
logger = logging.getLogger(__name__)
|
|
47
|
+
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
# Conditional MAF imports — fall back to local stubs when agent_framework
|
|
50
|
+
# is not installed so the module remains importable for testing / linting.
|
|
51
|
+
# ---------------------------------------------------------------------------
|
|
52
|
+
try:
|
|
53
|
+
from agent_framework import (
|
|
54
|
+
AgentContext,
|
|
55
|
+
AgentMiddleware,
|
|
56
|
+
AgentResponse,
|
|
57
|
+
FunctionInvocationContext,
|
|
58
|
+
FunctionMiddleware,
|
|
59
|
+
Message,
|
|
60
|
+
MiddlewareTermination,
|
|
61
|
+
)
|
|
62
|
+
except ImportError: # pragma: no cover
|
|
63
|
+
logger.debug(
|
|
64
|
+
"agent_framework is not installed; MAF middleware classes will use "
|
|
65
|
+
"protocol-only base stubs."
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
class AgentMiddleware: # type: ignore[no-redef]
|
|
69
|
+
"""Stub base class when agent_framework is absent."""
|
|
70
|
+
|
|
71
|
+
class FunctionMiddleware: # type: ignore[no-redef]
|
|
72
|
+
"""Stub base class when agent_framework is absent."""
|
|
73
|
+
|
|
74
|
+
class AgentContext: # type: ignore[no-redef]
|
|
75
|
+
"""Stub for type hints."""
|
|
76
|
+
|
|
77
|
+
class FunctionInvocationContext: # type: ignore[no-redef]
|
|
78
|
+
"""Stub for type hints."""
|
|
79
|
+
|
|
80
|
+
class AgentResponse: # type: ignore[no-redef]
|
|
81
|
+
def __init__(self, *, messages: list[Any] | None = None) -> None:
|
|
82
|
+
self.messages = messages or []
|
|
83
|
+
|
|
84
|
+
class Message: # type: ignore[no-redef]
|
|
85
|
+
def __init__(self, role: str, contents: list[str] | None = None) -> None:
|
|
86
|
+
self.role = role
|
|
87
|
+
self.contents = contents or []
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def text(self) -> str:
|
|
91
|
+
return str(self.contents[0]) if self.contents else ""
|
|
92
|
+
|
|
93
|
+
class MiddlewareTermination(Exception): # type: ignore[no-redef]
|
|
94
|
+
"""Local fallback when agent_framework is not installed."""
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
98
|
+
# 1. GovernancePolicyMiddleware
|
|
99
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class GovernancePolicyMiddleware(AgentMiddleware):
|
|
103
|
+
"""AgentMiddleware that evaluates declarative governance policies.
|
|
104
|
+
|
|
105
|
+
Intercepts every agent invocation, builds a context dict from the
|
|
106
|
+
incoming messages and agent metadata, and evaluates it against loaded
|
|
107
|
+
:class:`~agent_os.policies.PolicyEvaluator` rules. Denied requests
|
|
108
|
+
are short-circuited with an ``AgentResponse`` explaining the
|
|
109
|
+
violation and a ``MiddlewareTermination`` is raised.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
evaluator: Pre-configured :class:`PolicyEvaluator` with loaded
|
|
113
|
+
policy documents.
|
|
114
|
+
audit_log: Optional :class:`AuditLog` for recording decisions.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
def __init__(
|
|
118
|
+
self,
|
|
119
|
+
evaluator: PolicyEvaluator,
|
|
120
|
+
audit_log: AuditLog | None = None,
|
|
121
|
+
) -> None:
|
|
122
|
+
self.evaluator = evaluator
|
|
123
|
+
self.audit_log = audit_log
|
|
124
|
+
|
|
125
|
+
async def process(
|
|
126
|
+
self,
|
|
127
|
+
context: AgentContext,
|
|
128
|
+
call_next: Callable[[], Awaitable[None]],
|
|
129
|
+
) -> None:
|
|
130
|
+
"""Evaluate governance policy before agent execution."""
|
|
131
|
+
agent_name = getattr(context.agent, "name", "unknown")
|
|
132
|
+
|
|
133
|
+
# Extract the last user message text (handle empty conversations).
|
|
134
|
+
last_message_text = ""
|
|
135
|
+
messages: list[Any] = getattr(context, "messages", None) or []
|
|
136
|
+
if messages:
|
|
137
|
+
last_msg = messages[-1]
|
|
138
|
+
# Message.text is the MAF accessor; fall back to str()
|
|
139
|
+
last_message_text = (
|
|
140
|
+
getattr(last_msg, "text", None) or str(last_msg)
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Build context dict for the policy evaluator.
|
|
144
|
+
eval_context: dict[str, Any] = {
|
|
145
|
+
"agent": agent_name,
|
|
146
|
+
"message": last_message_text,
|
|
147
|
+
"timestamp": time.time(),
|
|
148
|
+
"stream": getattr(context, "stream", False),
|
|
149
|
+
"message_count": len(messages),
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
decision: PolicyDecision = self.evaluator.evaluate(eval_context)
|
|
153
|
+
|
|
154
|
+
# Persist the decision in the MAF metadata for downstream middleware.
|
|
155
|
+
metadata: dict[str, Any] = getattr(context, "metadata", {})
|
|
156
|
+
metadata["governance_decision"] = decision
|
|
157
|
+
|
|
158
|
+
if not decision.allowed:
|
|
159
|
+
logger.info(
|
|
160
|
+
"Policy DENY for agent '%s': %s (rule=%s)",
|
|
161
|
+
agent_name,
|
|
162
|
+
decision.reason,
|
|
163
|
+
decision.matched_rule,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
# Set a user-visible response explaining the denial.
|
|
167
|
+
context.result = AgentResponse(
|
|
168
|
+
messages=[
|
|
169
|
+
Message(
|
|
170
|
+
"assistant",
|
|
171
|
+
[f"⛔ Policy violation: {decision.reason}"],
|
|
172
|
+
)
|
|
173
|
+
]
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
if self.audit_log:
|
|
177
|
+
self.audit_log.log(
|
|
178
|
+
event_type="policy_violation",
|
|
179
|
+
agent_did=agent_name,
|
|
180
|
+
action="deny",
|
|
181
|
+
data={
|
|
182
|
+
"reason": decision.reason,
|
|
183
|
+
"matched_rule": decision.matched_rule,
|
|
184
|
+
"message_preview": last_message_text[:200],
|
|
185
|
+
},
|
|
186
|
+
outcome="denied",
|
|
187
|
+
policy_decision=decision.action,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
raise MiddlewareTermination(decision.reason)
|
|
191
|
+
|
|
192
|
+
# Policy allowed — log and continue the pipeline.
|
|
193
|
+
logger.debug(
|
|
194
|
+
"Policy ALLOW for agent '%s' (rule=%s)",
|
|
195
|
+
agent_name,
|
|
196
|
+
decision.matched_rule,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
if self.audit_log:
|
|
200
|
+
self.audit_log.log(
|
|
201
|
+
event_type="policy_evaluation",
|
|
202
|
+
agent_did=agent_name,
|
|
203
|
+
action="allow",
|
|
204
|
+
data={
|
|
205
|
+
"matched_rule": decision.matched_rule,
|
|
206
|
+
"message_preview": last_message_text[:200],
|
|
207
|
+
},
|
|
208
|
+
outcome="success",
|
|
209
|
+
policy_decision=decision.action,
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
await call_next()
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
216
|
+
# 2. CapabilityGuardMiddleware
|
|
217
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class CapabilityGuardMiddleware(FunctionMiddleware):
|
|
221
|
+
"""FunctionMiddleware that enforces tool allow/deny lists.
|
|
222
|
+
|
|
223
|
+
Each tool invocation is checked against explicit ``allowed_tools``
|
|
224
|
+
and ``denied_tools`` lists. If a tool is not permitted, the
|
|
225
|
+
function result is set to an error string and
|
|
226
|
+
``MiddlewareTermination`` is raised.
|
|
227
|
+
|
|
228
|
+
When both ``allowed_tools`` and ``denied_tools`` are provided,
|
|
229
|
+
``denied_tools`` takes precedence (a tool in both lists is denied).
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
allowed_tools: Whitelist of permitted tool names. If ``None``,
|
|
233
|
+
all tools are allowed unless explicitly denied.
|
|
234
|
+
denied_tools: Blacklist of forbidden tool names.
|
|
235
|
+
audit_log: Optional :class:`AuditLog` for recording tool
|
|
236
|
+
invocations.
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
def __init__(
|
|
240
|
+
self,
|
|
241
|
+
allowed_tools: list[str] | None = None,
|
|
242
|
+
denied_tools: list[str] | None = None,
|
|
243
|
+
audit_log: AuditLog | None = None,
|
|
244
|
+
) -> None:
|
|
245
|
+
self.allowed_tools = allowed_tools
|
|
246
|
+
self.denied_tools = denied_tools
|
|
247
|
+
self.audit_log = audit_log
|
|
248
|
+
|
|
249
|
+
def _is_denied(self, tool_name: str) -> bool:
|
|
250
|
+
"""Return ``True`` if *tool_name* should be blocked."""
|
|
251
|
+
# Explicit deny list takes precedence.
|
|
252
|
+
if self.denied_tools and tool_name in self.denied_tools:
|
|
253
|
+
return True
|
|
254
|
+
# If an allow list is set, anything not in it is denied.
|
|
255
|
+
if self.allowed_tools is not None and tool_name not in self.allowed_tools:
|
|
256
|
+
return True
|
|
257
|
+
return False
|
|
258
|
+
|
|
259
|
+
async def process(
|
|
260
|
+
self,
|
|
261
|
+
context: FunctionInvocationContext,
|
|
262
|
+
call_next: Callable[[], Awaitable[None]],
|
|
263
|
+
) -> None:
|
|
264
|
+
"""Guard tool invocations against capability policy."""
|
|
265
|
+
func_name = getattr(
|
|
266
|
+
getattr(context, "function", None), "name", "unknown"
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
if self._is_denied(func_name):
|
|
270
|
+
logger.info("Capability DENY: tool '%s' blocked by policy", func_name)
|
|
271
|
+
|
|
272
|
+
context.result = (
|
|
273
|
+
f"⛔ Tool '{func_name}' is not permitted by governance policy"
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
if self.audit_log:
|
|
277
|
+
self.audit_log.log(
|
|
278
|
+
event_type="tool_blocked",
|
|
279
|
+
agent_did="capability-guard",
|
|
280
|
+
action="deny",
|
|
281
|
+
resource=func_name,
|
|
282
|
+
data={"tool": func_name},
|
|
283
|
+
outcome="denied",
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
raise MiddlewareTermination(
|
|
287
|
+
f"Tool '{func_name}' is not permitted by governance policy"
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
# Tool is allowed — log start, execute, log completion.
|
|
291
|
+
if self.audit_log:
|
|
292
|
+
self.audit_log.log(
|
|
293
|
+
event_type="tool_invocation",
|
|
294
|
+
agent_did="capability-guard",
|
|
295
|
+
action="start",
|
|
296
|
+
resource=func_name,
|
|
297
|
+
data={"tool": func_name},
|
|
298
|
+
outcome="success",
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
logger.debug("Capability ALLOW: invoking tool '%s'", func_name)
|
|
302
|
+
|
|
303
|
+
await call_next()
|
|
304
|
+
|
|
305
|
+
# Log completion with a truncated result summary.
|
|
306
|
+
result_summary = str(getattr(context, "result", ""))[:500]
|
|
307
|
+
if self.audit_log:
|
|
308
|
+
self.audit_log.log(
|
|
309
|
+
event_type="tool_invocation",
|
|
310
|
+
agent_did="capability-guard",
|
|
311
|
+
action="complete",
|
|
312
|
+
resource=func_name,
|
|
313
|
+
data={
|
|
314
|
+
"tool": func_name,
|
|
315
|
+
"result_preview": result_summary,
|
|
316
|
+
},
|
|
317
|
+
outcome="success",
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
322
|
+
# 3. AuditTrailMiddleware
|
|
323
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
class AuditTrailMiddleware(AgentMiddleware):
|
|
327
|
+
"""AgentMiddleware that records tamper-proof audit entries.
|
|
328
|
+
|
|
329
|
+
Wraps every agent invocation with pre- and post-execution audit
|
|
330
|
+
entries, capturing timing information and the execution outcome.
|
|
331
|
+
The resulting :class:`AuditEntry` ID is stored in
|
|
332
|
+
``context.metadata["audit_entry_id"]`` for downstream correlation.
|
|
333
|
+
|
|
334
|
+
Args:
|
|
335
|
+
audit_log: :class:`AuditLog` instance for recording entries.
|
|
336
|
+
agent_did: Optional decentralised identifier for the agent.
|
|
337
|
+
Defaults to the MAF agent name when not provided.
|
|
338
|
+
"""
|
|
339
|
+
|
|
340
|
+
def __init__(
|
|
341
|
+
self,
|
|
342
|
+
audit_log: AuditLog,
|
|
343
|
+
agent_did: str | None = None,
|
|
344
|
+
) -> None:
|
|
345
|
+
self.audit_log = audit_log
|
|
346
|
+
self.agent_did = agent_did
|
|
347
|
+
|
|
348
|
+
async def process(
|
|
349
|
+
self,
|
|
350
|
+
context: AgentContext,
|
|
351
|
+
call_next: Callable[[], Awaitable[None]],
|
|
352
|
+
) -> None:
|
|
353
|
+
"""Record pre/post execution audit entries with timing."""
|
|
354
|
+
agent_name = getattr(context.agent, "name", "unknown")
|
|
355
|
+
did = self.agent_did or agent_name
|
|
356
|
+
|
|
357
|
+
messages: list[Any] = getattr(context, "messages", None) or []
|
|
358
|
+
metadata: dict[str, Any] = getattr(context, "metadata", {})
|
|
359
|
+
|
|
360
|
+
# Pre-execution audit entry.
|
|
361
|
+
start_entry: AuditEntry = self.audit_log.log(
|
|
362
|
+
event_type="agent_invocation",
|
|
363
|
+
agent_did=did,
|
|
364
|
+
action="start",
|
|
365
|
+
data={
|
|
366
|
+
"agent_name": agent_name,
|
|
367
|
+
"message_count": len(messages),
|
|
368
|
+
"stream": getattr(context, "stream", False),
|
|
369
|
+
},
|
|
370
|
+
outcome="success",
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
# Store the entry ID for downstream middleware / callers.
|
|
374
|
+
metadata["audit_entry_id"] = start_entry.entry_id
|
|
375
|
+
|
|
376
|
+
start_time = time.time()
|
|
377
|
+
outcome = "success"
|
|
378
|
+
error_detail: str | None = None
|
|
379
|
+
|
|
380
|
+
try:
|
|
381
|
+
await call_next()
|
|
382
|
+
except Exception as exc:
|
|
383
|
+
outcome = "error"
|
|
384
|
+
error_detail = f"{type(exc).__name__}: {exc}"
|
|
385
|
+
raise
|
|
386
|
+
finally:
|
|
387
|
+
elapsed = time.time() - start_time
|
|
388
|
+
|
|
389
|
+
# Post-execution audit entry.
|
|
390
|
+
self.audit_log.log(
|
|
391
|
+
event_type="agent_invocation",
|
|
392
|
+
agent_did=did,
|
|
393
|
+
action="complete",
|
|
394
|
+
data={
|
|
395
|
+
"agent_name": agent_name,
|
|
396
|
+
"elapsed_seconds": round(elapsed, 4),
|
|
397
|
+
"start_entry_id": start_entry.entry_id,
|
|
398
|
+
**({"error": error_detail} if error_detail else {}),
|
|
399
|
+
},
|
|
400
|
+
outcome=outcome,
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
logger.debug(
|
|
404
|
+
"Audit: agent '%s' completed in %.3fs (outcome=%s)",
|
|
405
|
+
agent_name,
|
|
406
|
+
elapsed,
|
|
407
|
+
outcome,
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
412
|
+
# 4. RogueDetectionMiddleware
|
|
413
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
class RogueDetectionMiddleware(FunctionMiddleware):
|
|
417
|
+
"""FunctionMiddleware that detects rogue agent behaviour.
|
|
418
|
+
|
|
419
|
+
Feeds every tool invocation into a
|
|
420
|
+
:class:`~agent_sre.anomaly.RogueAgentDetector` and checks the
|
|
421
|
+
resulting risk assessment. High-risk agents are blocked with a
|
|
422
|
+
``MiddlewareTermination``; medium-risk invocations proceed with a
|
|
423
|
+
warning logged to the audit trail.
|
|
424
|
+
|
|
425
|
+
Args:
|
|
426
|
+
detector: Pre-configured :class:`RogueAgentDetector`.
|
|
427
|
+
agent_id: Identifier for the agent being monitored.
|
|
428
|
+
capability_profile: Optional dict mapping ``"allowed_tools"``
|
|
429
|
+
to a list of expected tool names. Registered with the
|
|
430
|
+
detector on construction.
|
|
431
|
+
audit_log: Optional :class:`AuditLog` for recording detections.
|
|
432
|
+
"""
|
|
433
|
+
|
|
434
|
+
def __init__(
|
|
435
|
+
self,
|
|
436
|
+
detector: RogueAgentDetector,
|
|
437
|
+
agent_id: str,
|
|
438
|
+
capability_profile: dict[str, Any] | None = None,
|
|
439
|
+
audit_log: AuditLog | None = None,
|
|
440
|
+
) -> None:
|
|
441
|
+
self.detector = detector
|
|
442
|
+
self.agent_id = agent_id
|
|
443
|
+
self.audit_log = audit_log
|
|
444
|
+
|
|
445
|
+
# Register the expected capability profile if provided.
|
|
446
|
+
if capability_profile and "allowed_tools" in capability_profile:
|
|
447
|
+
self.detector.register_capability_profile(
|
|
448
|
+
agent_id,
|
|
449
|
+
capability_profile["allowed_tools"],
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
async def process(
|
|
453
|
+
self,
|
|
454
|
+
context: FunctionInvocationContext,
|
|
455
|
+
call_next: Callable[[], Awaitable[None]],
|
|
456
|
+
) -> None:
|
|
457
|
+
"""Assess rogue risk before allowing tool execution."""
|
|
458
|
+
func_name = getattr(
|
|
459
|
+
getattr(context, "function", None), "name", "unknown"
|
|
460
|
+
)
|
|
461
|
+
now = time.time()
|
|
462
|
+
|
|
463
|
+
# Feed the observation into the detector's analyzers.
|
|
464
|
+
self.detector.record_action(
|
|
465
|
+
agent_id=self.agent_id,
|
|
466
|
+
action=func_name,
|
|
467
|
+
tool_name=func_name,
|
|
468
|
+
timestamp=now,
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
# Produce a composite risk assessment.
|
|
472
|
+
assessment = self.detector.assess(self.agent_id, timestamp=now)
|
|
473
|
+
|
|
474
|
+
if assessment.quarantine_recommended:
|
|
475
|
+
logger.warning(
|
|
476
|
+
"Rogue QUARANTINE for agent '%s': risk=%s score=%.2f",
|
|
477
|
+
self.agent_id,
|
|
478
|
+
assessment.risk_level.value,
|
|
479
|
+
assessment.composite_score,
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
context.result = (
|
|
483
|
+
f"⛔ Agent '{self.agent_id}' has been quarantined due to "
|
|
484
|
+
f"anomalous behaviour (risk={assessment.risk_level.value}, "
|
|
485
|
+
f"score={assessment.composite_score:.2f})"
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
if self.audit_log:
|
|
489
|
+
self.audit_log.log(
|
|
490
|
+
event_type="rogue_detection",
|
|
491
|
+
agent_did=self.agent_id,
|
|
492
|
+
action="quarantine",
|
|
493
|
+
resource=func_name,
|
|
494
|
+
data=assessment.to_dict(),
|
|
495
|
+
outcome="denied",
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
raise MiddlewareTermination(
|
|
499
|
+
f"Agent '{self.agent_id}' quarantined: "
|
|
500
|
+
f"risk={assessment.risk_level.value}"
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
# Log a warning for MEDIUM or above but allow execution.
|
|
504
|
+
if assessment.risk_level in (RiskLevel.MEDIUM, RiskLevel.HIGH):
|
|
505
|
+
logger.warning(
|
|
506
|
+
"Rogue WARNING for agent '%s': risk=%s score=%.2f "
|
|
507
|
+
"(tool=%s)",
|
|
508
|
+
self.agent_id,
|
|
509
|
+
assessment.risk_level.value,
|
|
510
|
+
assessment.composite_score,
|
|
511
|
+
func_name,
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
if self.audit_log:
|
|
515
|
+
self.audit_log.log(
|
|
516
|
+
event_type="rogue_detection",
|
|
517
|
+
agent_did=self.agent_id,
|
|
518
|
+
action="warning",
|
|
519
|
+
resource=func_name,
|
|
520
|
+
data=assessment.to_dict(),
|
|
521
|
+
outcome="success",
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
await call_next()
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
528
|
+
# Convenience factory
|
|
529
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
def create_governance_middleware(
|
|
533
|
+
policy_directory: str | Path | None = None,
|
|
534
|
+
allowed_tools: list[str] | None = None,
|
|
535
|
+
denied_tools: list[str] | None = None,
|
|
536
|
+
agent_id: str = "default-agent",
|
|
537
|
+
enable_rogue_detection: bool = True,
|
|
538
|
+
audit_log: AuditLog | None = None,
|
|
539
|
+
) -> list:
|
|
540
|
+
"""Create a complete governance middleware stack for a MAF agent.
|
|
541
|
+
|
|
542
|
+
Assembles and returns an ordered list of middleware instances ready
|
|
543
|
+
to pass directly to a MAF ``Agent(middleware=...)`` constructor.
|
|
544
|
+
|
|
545
|
+
The stack is built bottom-up:
|
|
546
|
+
|
|
547
|
+
1. :class:`AuditTrailMiddleware` (if *audit_log* provided)
|
|
548
|
+
2. :class:`GovernancePolicyMiddleware` (if *policy_directory* provided)
|
|
549
|
+
3. :class:`CapabilityGuardMiddleware` (if allow/deny lists provided)
|
|
550
|
+
4. :class:`RogueDetectionMiddleware` (if *enable_rogue_detection*)
|
|
551
|
+
|
|
552
|
+
Args:
|
|
553
|
+
policy_directory: Path to a directory of YAML policy files.
|
|
554
|
+
When provided, a :class:`PolicyEvaluator` is created and
|
|
555
|
+
loaded with all ``*.yaml`` / ``*.yml`` files found.
|
|
556
|
+
allowed_tools: Whitelist of permitted tool names.
|
|
557
|
+
denied_tools: Blacklist of forbidden tool names.
|
|
558
|
+
agent_id: Identifier for the agent (used by audit and rogue
|
|
559
|
+
detection).
|
|
560
|
+
enable_rogue_detection: Whether to include the
|
|
561
|
+
:class:`RogueDetectionMiddleware`.
|
|
562
|
+
audit_log: Shared :class:`AuditLog` instance. When ``None``,
|
|
563
|
+
a fresh in-memory log is created if any auditing middleware
|
|
564
|
+
is needed.
|
|
565
|
+
|
|
566
|
+
Returns:
|
|
567
|
+
List of middleware instances in recommended execution order.
|
|
568
|
+
|
|
569
|
+
Example::
|
|
570
|
+
|
|
571
|
+
from agent_framework import Agent
|
|
572
|
+
from agent_os.integrations.maf_adapter import create_governance_middleware
|
|
573
|
+
|
|
574
|
+
stack = create_governance_middleware(
|
|
575
|
+
policy_directory="policies/",
|
|
576
|
+
allowed_tools=["search", "read_file"],
|
|
577
|
+
agent_id="my-researcher",
|
|
578
|
+
)
|
|
579
|
+
agent = Agent(name="researcher", middleware=stack)
|
|
580
|
+
"""
|
|
581
|
+
stack: list[Any] = []
|
|
582
|
+
|
|
583
|
+
# Determine whether we need an audit log for any layer.
|
|
584
|
+
needs_audit = (
|
|
585
|
+
audit_log is not None
|
|
586
|
+
or policy_directory is not None
|
|
587
|
+
or allowed_tools is not None
|
|
588
|
+
or denied_tools is not None
|
|
589
|
+
or enable_rogue_detection
|
|
590
|
+
)
|
|
591
|
+
if needs_audit and audit_log is None:
|
|
592
|
+
audit_log = AuditLog()
|
|
593
|
+
|
|
594
|
+
# 1. Audit trail (outermost — captures everything).
|
|
595
|
+
if audit_log is not None:
|
|
596
|
+
stack.append(AuditTrailMiddleware(audit_log=audit_log, agent_did=agent_id))
|
|
597
|
+
|
|
598
|
+
# 2. Governance policy enforcement.
|
|
599
|
+
if policy_directory is not None:
|
|
600
|
+
evaluator = PolicyEvaluator()
|
|
601
|
+
evaluator.load_policies(policy_directory)
|
|
602
|
+
stack.append(
|
|
603
|
+
GovernancePolicyMiddleware(evaluator=evaluator, audit_log=audit_log)
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
# 3. Capability guard.
|
|
607
|
+
if allowed_tools is not None or denied_tools is not None:
|
|
608
|
+
stack.append(
|
|
609
|
+
CapabilityGuardMiddleware(
|
|
610
|
+
allowed_tools=allowed_tools,
|
|
611
|
+
denied_tools=denied_tools,
|
|
612
|
+
audit_log=audit_log,
|
|
613
|
+
)
|
|
614
|
+
)
|
|
615
|
+
|
|
616
|
+
# 4. Rogue detection (innermost — closest to actual tool execution).
|
|
617
|
+
if enable_rogue_detection:
|
|
618
|
+
detector = RogueAgentDetector(config=RogueDetectorConfig())
|
|
619
|
+
capability_profile: dict[str, Any] | None = None
|
|
620
|
+
if allowed_tools:
|
|
621
|
+
capability_profile = {"allowed_tools": allowed_tools}
|
|
622
|
+
stack.append(
|
|
623
|
+
RogueDetectionMiddleware(
|
|
624
|
+
detector=detector,
|
|
625
|
+
agent_id=agent_id,
|
|
626
|
+
capability_profile=capability_profile,
|
|
627
|
+
audit_log=audit_log,
|
|
628
|
+
)
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
return stack
|