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,406 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""
|
|
4
|
+
OpenAI Agents SDK Integration for Agent-OS
|
|
5
|
+
============================================
|
|
6
|
+
|
|
7
|
+
Provides kernel-level governance for OpenAI Agents SDK workflows.
|
|
8
|
+
|
|
9
|
+
Features:
|
|
10
|
+
- Policy enforcement for agent tool calls
|
|
11
|
+
- Guardrails integration with Agent-OS policies
|
|
12
|
+
- Handoff monitoring and approval
|
|
13
|
+
- Streaming response governance
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
>>> from agent_os.integrations.openai_agents import OpenAIAgentsKernel
|
|
17
|
+
>>> from agent_os.policies import GovernancePolicy
|
|
18
|
+
>>> from agents import Agent, Runner
|
|
19
|
+
>>>
|
|
20
|
+
>>> # Create governed kernel
|
|
21
|
+
>>> policy = GovernancePolicy(
|
|
22
|
+
... max_tool_calls=10,
|
|
23
|
+
... allowed_tools=["file_search", "code_interpreter"],
|
|
24
|
+
... blocked_patterns=["rm -rf", "DROP TABLE"],
|
|
25
|
+
... )
|
|
26
|
+
>>> kernel = OpenAIAgentsKernel(policy=policy)
|
|
27
|
+
>>>
|
|
28
|
+
>>> # Wrap agent
|
|
29
|
+
>>> agent = Agent(name="assistant", model="gpt-4o")
|
|
30
|
+
>>> governed_agent = kernel.wrap(agent)
|
|
31
|
+
>>>
|
|
32
|
+
>>> # Run with governance
|
|
33
|
+
>>> result = await Runner.run(governed_agent, "Analyze this data")
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
from __future__ import annotations
|
|
37
|
+
|
|
38
|
+
import asyncio
|
|
39
|
+
import logging
|
|
40
|
+
import time
|
|
41
|
+
from dataclasses import dataclass, field
|
|
42
|
+
from datetime import datetime, timezone
|
|
43
|
+
from functools import wraps
|
|
44
|
+
from typing import Any, Callable
|
|
45
|
+
|
|
46
|
+
logger = logging.getLogger(__name__)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class GovernancePolicy:
|
|
51
|
+
"""Policy configuration for OpenAI Agents."""
|
|
52
|
+
# Tool limits
|
|
53
|
+
max_tool_calls: int = 50
|
|
54
|
+
max_handoffs: int = 5
|
|
55
|
+
timeout_seconds: int = 300
|
|
56
|
+
|
|
57
|
+
# Tool filtering
|
|
58
|
+
allowed_tools: list[str] = field(default_factory=list)
|
|
59
|
+
blocked_tools: list[str] = field(default_factory=list)
|
|
60
|
+
|
|
61
|
+
# Content filtering
|
|
62
|
+
blocked_patterns: list[str] = field(default_factory=list)
|
|
63
|
+
pii_detection: bool = True
|
|
64
|
+
|
|
65
|
+
# Approval flows
|
|
66
|
+
require_human_approval: bool = False
|
|
67
|
+
approval_threshold: float = 0.8
|
|
68
|
+
|
|
69
|
+
# Audit
|
|
70
|
+
log_all_calls: bool = True
|
|
71
|
+
checkpoint_frequency: int = 5
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@dataclass
|
|
75
|
+
class ExecutionContext:
|
|
76
|
+
"""Runtime context for governed execution."""
|
|
77
|
+
session_id: str
|
|
78
|
+
agent_id: str
|
|
79
|
+
policy: GovernancePolicy
|
|
80
|
+
|
|
81
|
+
# Counters
|
|
82
|
+
tool_calls: int = 0
|
|
83
|
+
handoffs: int = 0
|
|
84
|
+
|
|
85
|
+
# Timing
|
|
86
|
+
started_at: datetime = field(default_factory=datetime.utcnow)
|
|
87
|
+
|
|
88
|
+
# Audit trail
|
|
89
|
+
events: list[dict[str, Any]] = field(default_factory=list)
|
|
90
|
+
|
|
91
|
+
def record_event(self, event_type: str, data: dict[str, Any]) -> None:
|
|
92
|
+
self.events.append({
|
|
93
|
+
"type": event_type,
|
|
94
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
95
|
+
"data": data,
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class PolicyViolationError(Exception):
|
|
100
|
+
"""Raised when a policy violation is detected."""
|
|
101
|
+
def __init__(self, policy_name: str, description: str, severity: str = "high"):
|
|
102
|
+
self.policy_name = policy_name
|
|
103
|
+
self.description = description
|
|
104
|
+
self.severity = severity
|
|
105
|
+
super().__init__(f"Policy violation ({policy_name}): {description}")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class OpenAIAgentsKernel:
|
|
109
|
+
"""
|
|
110
|
+
Governance kernel for OpenAI Agents SDK.
|
|
111
|
+
|
|
112
|
+
Wraps Agent and Runner to enforce policies during execution.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
def __init__(
|
|
116
|
+
self,
|
|
117
|
+
policy: GovernancePolicy | None = None,
|
|
118
|
+
on_violation: Callable[[PolicyViolationError], None] | None = None,
|
|
119
|
+
) -> None:
|
|
120
|
+
self.policy: GovernancePolicy = policy or GovernancePolicy()
|
|
121
|
+
self.on_violation: Callable[[PolicyViolationError], None] = (
|
|
122
|
+
on_violation or self._default_violation_handler
|
|
123
|
+
)
|
|
124
|
+
self._contexts: dict[str, ExecutionContext] = {}
|
|
125
|
+
self._wrapped_agents: dict[str, Any] = {}
|
|
126
|
+
self._start_time: float = time.monotonic()
|
|
127
|
+
self._last_error: str | None = None
|
|
128
|
+
|
|
129
|
+
def _default_violation_handler(self, error: PolicyViolationError) -> None:
|
|
130
|
+
logger.error(f"Policy violation: {error}")
|
|
131
|
+
|
|
132
|
+
def _create_context(self, agent_id: str) -> ExecutionContext:
|
|
133
|
+
"""Create execution context for an agent."""
|
|
134
|
+
import uuid
|
|
135
|
+
session_id = str(uuid.uuid4())[:8]
|
|
136
|
+
ctx = ExecutionContext(
|
|
137
|
+
session_id=session_id,
|
|
138
|
+
agent_id=agent_id,
|
|
139
|
+
policy=self.policy,
|
|
140
|
+
)
|
|
141
|
+
self._contexts[session_id] = ctx
|
|
142
|
+
return ctx
|
|
143
|
+
|
|
144
|
+
def _check_tool_allowed(self, tool_name: str) -> tuple[bool, str]:
|
|
145
|
+
"""Check if tool is allowed by policy."""
|
|
146
|
+
# Check blocked list
|
|
147
|
+
if tool_name in self.policy.blocked_tools:
|
|
148
|
+
return False, f"Tool '{tool_name}' is blocked by policy"
|
|
149
|
+
|
|
150
|
+
# Check allowed list (if specified)
|
|
151
|
+
if self.policy.allowed_tools:
|
|
152
|
+
if tool_name not in self.policy.allowed_tools:
|
|
153
|
+
return False, f"Tool '{tool_name}' not in allowed list"
|
|
154
|
+
|
|
155
|
+
return True, ""
|
|
156
|
+
|
|
157
|
+
def _check_content(self, content: str) -> tuple[bool, str]:
|
|
158
|
+
"""Check content against blocked patterns."""
|
|
159
|
+
content_lower = content.lower()
|
|
160
|
+
for pattern in self.policy.blocked_patterns:
|
|
161
|
+
if pattern.lower() in content_lower:
|
|
162
|
+
return False, f"Content matches blocked pattern: {pattern}"
|
|
163
|
+
return True, ""
|
|
164
|
+
|
|
165
|
+
def wrap(self, agent: Any) -> Any:
|
|
166
|
+
"""
|
|
167
|
+
Wrap an OpenAI Agent with governance.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
agent: OpenAI Agents SDK Agent instance
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
Governed agent wrapper
|
|
174
|
+
"""
|
|
175
|
+
agent_id = getattr(agent, "name", str(id(agent)))
|
|
176
|
+
|
|
177
|
+
# Create wrapper class
|
|
178
|
+
class GovernedAgent:
|
|
179
|
+
def __init__(wrapper_self, original: Any, kernel: OpenAIAgentsKernel):
|
|
180
|
+
wrapper_self._original = original
|
|
181
|
+
wrapper_self._kernel = kernel
|
|
182
|
+
wrapper_self._context = kernel._create_context(agent_id)
|
|
183
|
+
|
|
184
|
+
# Copy attributes
|
|
185
|
+
for attr in ["name", "model", "instructions", "tools"]:
|
|
186
|
+
if hasattr(original, attr):
|
|
187
|
+
setattr(wrapper_self, attr, getattr(original, attr))
|
|
188
|
+
|
|
189
|
+
@property
|
|
190
|
+
def original(wrapper_self) -> Any:
|
|
191
|
+
return wrapper_self._original
|
|
192
|
+
|
|
193
|
+
def __getattr__(wrapper_self, name: str) -> Any:
|
|
194
|
+
return getattr(wrapper_self._original, name)
|
|
195
|
+
|
|
196
|
+
wrapped = GovernedAgent(agent, self)
|
|
197
|
+
self._wrapped_agents[agent_id] = wrapped
|
|
198
|
+
logger.info(f"Wrapped agent '{agent_id}' with governance kernel")
|
|
199
|
+
return wrapped
|
|
200
|
+
|
|
201
|
+
def unwrap(self, governed_agent: Any) -> Any:
|
|
202
|
+
"""Remove governance wrapper."""
|
|
203
|
+
if hasattr(governed_agent, "_original"):
|
|
204
|
+
return governed_agent._original
|
|
205
|
+
return governed_agent
|
|
206
|
+
|
|
207
|
+
def wrap_runner(self, runner_class: Any) -> Any:
|
|
208
|
+
"""
|
|
209
|
+
Wrap the Runner class to intercept executions.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
runner_class: OpenAI Agents SDK Runner class
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
Governed Runner class
|
|
216
|
+
"""
|
|
217
|
+
kernel = self
|
|
218
|
+
|
|
219
|
+
class GovernedRunner:
|
|
220
|
+
@classmethod
|
|
221
|
+
async def run(
|
|
222
|
+
cls,
|
|
223
|
+
agent: Any,
|
|
224
|
+
input_text: str,
|
|
225
|
+
**kwargs,
|
|
226
|
+
) -> Any:
|
|
227
|
+
# Get context
|
|
228
|
+
ctx = None
|
|
229
|
+
if hasattr(agent, "_context"):
|
|
230
|
+
ctx = agent._context
|
|
231
|
+
|
|
232
|
+
# Pre-execution checks
|
|
233
|
+
if ctx:
|
|
234
|
+
# Check content
|
|
235
|
+
ok, reason = kernel._check_content(input_text)
|
|
236
|
+
if not ok:
|
|
237
|
+
error = PolicyViolationError("content_filter", reason)
|
|
238
|
+
kernel.on_violation(error)
|
|
239
|
+
if kernel.policy.require_human_approval:
|
|
240
|
+
raise error
|
|
241
|
+
|
|
242
|
+
ctx.record_event("run_start", {"input_length": len(input_text)})
|
|
243
|
+
|
|
244
|
+
# Get original agent
|
|
245
|
+
original_agent = agent
|
|
246
|
+
if hasattr(agent, "_original"):
|
|
247
|
+
original_agent = agent._original
|
|
248
|
+
|
|
249
|
+
# Run with monitoring
|
|
250
|
+
try:
|
|
251
|
+
result = await runner_class.run(original_agent, input_text, **kwargs)
|
|
252
|
+
|
|
253
|
+
if ctx:
|
|
254
|
+
ctx.record_event("run_complete", {"success": True})
|
|
255
|
+
|
|
256
|
+
return result
|
|
257
|
+
except Exception as e:
|
|
258
|
+
if ctx:
|
|
259
|
+
ctx.record_event("run_error", {"error": str(e)})
|
|
260
|
+
raise
|
|
261
|
+
|
|
262
|
+
@classmethod
|
|
263
|
+
def run_sync(cls, agent: Any, input_text: str, **kwargs) -> Any:
|
|
264
|
+
return asyncio.run(cls.run(agent, input_text, **kwargs))
|
|
265
|
+
|
|
266
|
+
return GovernedRunner
|
|
267
|
+
|
|
268
|
+
def create_tool_guard(self) -> Callable:
|
|
269
|
+
"""
|
|
270
|
+
Create a tool execution guard.
|
|
271
|
+
|
|
272
|
+
Use as a decorator or wrapper for tool functions.
|
|
273
|
+
"""
|
|
274
|
+
kernel = self
|
|
275
|
+
|
|
276
|
+
def guard(func: Callable) -> Callable:
|
|
277
|
+
@wraps(func)
|
|
278
|
+
async def wrapper(*args, **kwargs):
|
|
279
|
+
tool_name = func.__name__
|
|
280
|
+
|
|
281
|
+
# Check if tool is allowed
|
|
282
|
+
ok, reason = kernel._check_tool_allowed(tool_name)
|
|
283
|
+
if not ok:
|
|
284
|
+
error = PolicyViolationError("tool_filter", reason)
|
|
285
|
+
kernel.on_violation(error)
|
|
286
|
+
raise error
|
|
287
|
+
|
|
288
|
+
# Check content in arguments
|
|
289
|
+
for arg in args:
|
|
290
|
+
if isinstance(arg, str):
|
|
291
|
+
ok, reason = kernel._check_content(arg)
|
|
292
|
+
if not ok:
|
|
293
|
+
error = PolicyViolationError("content_filter", reason)
|
|
294
|
+
kernel.on_violation(error)
|
|
295
|
+
raise error
|
|
296
|
+
|
|
297
|
+
for value in kwargs.values():
|
|
298
|
+
if isinstance(value, str):
|
|
299
|
+
ok, reason = kernel._check_content(value)
|
|
300
|
+
if not ok:
|
|
301
|
+
error = PolicyViolationError("content_filter", reason)
|
|
302
|
+
kernel.on_violation(error)
|
|
303
|
+
raise error
|
|
304
|
+
|
|
305
|
+
# Execute
|
|
306
|
+
if asyncio.iscoroutinefunction(func):
|
|
307
|
+
return await func(*args, **kwargs)
|
|
308
|
+
return func(*args, **kwargs)
|
|
309
|
+
|
|
310
|
+
return wrapper
|
|
311
|
+
return guard
|
|
312
|
+
|
|
313
|
+
def create_guardrail(self) -> Any:
|
|
314
|
+
"""
|
|
315
|
+
Create an OpenAI Agents SDK compatible guardrail.
|
|
316
|
+
|
|
317
|
+
Returns a guardrail that can be added to an agent.
|
|
318
|
+
"""
|
|
319
|
+
kernel = self
|
|
320
|
+
|
|
321
|
+
class PolicyGuardrail:
|
|
322
|
+
"""Agent-OS policy guardrail for OpenAI Agents SDK."""
|
|
323
|
+
|
|
324
|
+
async def __call__(
|
|
325
|
+
self,
|
|
326
|
+
context: Any,
|
|
327
|
+
agent: Any,
|
|
328
|
+
input_text: str,
|
|
329
|
+
) -> str | None:
|
|
330
|
+
"""
|
|
331
|
+
Check input against policies.
|
|
332
|
+
|
|
333
|
+
Returns None if allowed, or a rejection message if blocked.
|
|
334
|
+
"""
|
|
335
|
+
# Check content patterns
|
|
336
|
+
ok, reason = kernel._check_content(input_text)
|
|
337
|
+
if not ok:
|
|
338
|
+
logger.warning(f"Guardrail blocked: {reason}")
|
|
339
|
+
return f"Request blocked by policy: {reason}"
|
|
340
|
+
|
|
341
|
+
# Check tool calls if in context
|
|
342
|
+
if hasattr(context, "tool_calls"):
|
|
343
|
+
for tool_call in context.tool_calls:
|
|
344
|
+
tool_name = getattr(tool_call, "name", "")
|
|
345
|
+
ok, reason = kernel._check_tool_allowed(tool_name)
|
|
346
|
+
if not ok:
|
|
347
|
+
logger.warning(f"Guardrail blocked tool: {reason}")
|
|
348
|
+
return f"Tool blocked by policy: {reason}"
|
|
349
|
+
|
|
350
|
+
return None # Allowed
|
|
351
|
+
|
|
352
|
+
return PolicyGuardrail()
|
|
353
|
+
|
|
354
|
+
def get_context(self, session_id: str) -> ExecutionContext | None:
|
|
355
|
+
"""Get execution context by session ID."""
|
|
356
|
+
return self._contexts.get(session_id)
|
|
357
|
+
|
|
358
|
+
def get_audit_log(self, session_id: str) -> list[dict[str, Any]]:
|
|
359
|
+
"""Get audit log for a session."""
|
|
360
|
+
ctx = self._contexts.get(session_id)
|
|
361
|
+
if ctx:
|
|
362
|
+
return ctx.events
|
|
363
|
+
return []
|
|
364
|
+
|
|
365
|
+
def get_stats(self) -> dict[str, Any]:
|
|
366
|
+
"""Get governance statistics."""
|
|
367
|
+
total_tool_calls: int = sum(ctx.tool_calls for ctx in self._contexts.values())
|
|
368
|
+
total_handoffs: int = sum(ctx.handoffs for ctx in self._contexts.values())
|
|
369
|
+
|
|
370
|
+
return {
|
|
371
|
+
"total_sessions": len(self._contexts),
|
|
372
|
+
"wrapped_agents": len(self._wrapped_agents),
|
|
373
|
+
"total_tool_calls": total_tool_calls,
|
|
374
|
+
"total_handoffs": total_handoffs,
|
|
375
|
+
"policy": {
|
|
376
|
+
"max_tool_calls": self.policy.max_tool_calls,
|
|
377
|
+
"max_handoffs": self.policy.max_handoffs,
|
|
378
|
+
"blocked_tools": self.policy.blocked_tools,
|
|
379
|
+
},
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
def health_check(self) -> dict[str, Any]:
|
|
383
|
+
"""Return adapter health status.
|
|
384
|
+
|
|
385
|
+
Returns:
|
|
386
|
+
A dict with ``status``, ``backend``, ``last_error``, and
|
|
387
|
+
``uptime_seconds`` keys.
|
|
388
|
+
"""
|
|
389
|
+
uptime: float = time.monotonic() - self._start_time
|
|
390
|
+
status: str = "degraded" if self._last_error else "healthy"
|
|
391
|
+
return {
|
|
392
|
+
"status": status,
|
|
393
|
+
"backend": "openai_agents_sdk",
|
|
394
|
+
"backend_connected": bool(self._wrapped_agents),
|
|
395
|
+
"last_error": self._last_error,
|
|
396
|
+
"uptime_seconds": round(uptime, 2),
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
# Convenience exports
|
|
401
|
+
__all__ = [
|
|
402
|
+
"OpenAIAgentsKernel",
|
|
403
|
+
"GovernancePolicy",
|
|
404
|
+
"ExecutionContext",
|
|
405
|
+
"PolicyViolationError",
|
|
406
|
+
]
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""
|
|
4
|
+
Policy Inheritance and Composition
|
|
5
|
+
|
|
6
|
+
Utilities for merging, inheriting, and overriding GovernancePolicy instances.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from dataclasses import asdict, fields
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from .base import GovernancePolicy
|
|
15
|
+
|
|
16
|
+
# Fields that use "most restrictive" (min) merging
|
|
17
|
+
_MIN_FIELDS = {"max_tokens", "max_tool_calls", "max_concurrent", "timeout_seconds"}
|
|
18
|
+
|
|
19
|
+
# Fields that use "most restrictive" (truthy wins) merging
|
|
20
|
+
_BOOL_RESTRICTIVE = {"require_human_approval", "log_all_calls"}
|
|
21
|
+
|
|
22
|
+
# List fields with special merge semantics
|
|
23
|
+
_UNION_LIST_FIELDS = {"blocked_patterns"}
|
|
24
|
+
_INTERSECT_LIST_FIELDS = {"allowed_tools"}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def override_policy(base: GovernancePolicy, **kwargs: Any) -> GovernancePolicy:
|
|
28
|
+
"""Create a copy of *base* with the given field overrides applied."""
|
|
29
|
+
data = asdict(base)
|
|
30
|
+
data.update(kwargs)
|
|
31
|
+
return GovernancePolicy(**data)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def compose_policies(*policies: GovernancePolicy) -> GovernancePolicy:
|
|
35
|
+
"""Merge multiple policies using most-restrictive-wins semantics.
|
|
36
|
+
|
|
37
|
+
- ``max_tokens``, ``max_tool_calls``, ``max_concurrent``, ``timeout_seconds``:
|
|
38
|
+
smallest value wins.
|
|
39
|
+
- ``blocked_patterns``: union of all lists.
|
|
40
|
+
- ``allowed_tools``: intersection when both specify; single list kept as-is.
|
|
41
|
+
- ``version``: highest version string (lexicographic).
|
|
42
|
+
- ``name``: names joined with " + ".
|
|
43
|
+
- ``require_human_approval``, ``log_all_calls``: ``True`` if *any* policy
|
|
44
|
+
sets them.
|
|
45
|
+
"""
|
|
46
|
+
if not policies:
|
|
47
|
+
raise ValueError("compose_policies requires at least one policy")
|
|
48
|
+
if len(policies) == 1:
|
|
49
|
+
return override_policy(policies[0])
|
|
50
|
+
|
|
51
|
+
result = asdict(policies[0])
|
|
52
|
+
|
|
53
|
+
for policy in policies[1:]:
|
|
54
|
+
other = asdict(policy)
|
|
55
|
+
|
|
56
|
+
# Name
|
|
57
|
+
result["name"] = result["name"] + " + " + other["name"]
|
|
58
|
+
|
|
59
|
+
# Most-restrictive numeric fields
|
|
60
|
+
for f in _MIN_FIELDS:
|
|
61
|
+
result[f] = min(result[f], other[f])
|
|
62
|
+
|
|
63
|
+
# Boolean restrictive fields
|
|
64
|
+
for f in _BOOL_RESTRICTIVE:
|
|
65
|
+
result[f] = result[f] or other[f]
|
|
66
|
+
|
|
67
|
+
# Union list fields
|
|
68
|
+
for f in _UNION_LIST_FIELDS:
|
|
69
|
+
combined = list(result[f])
|
|
70
|
+
for item in other[f]:
|
|
71
|
+
if item not in combined:
|
|
72
|
+
combined.append(item)
|
|
73
|
+
result[f] = combined
|
|
74
|
+
|
|
75
|
+
# Intersect list fields (allowed_tools)
|
|
76
|
+
for f in _INTERSECT_LIST_FIELDS:
|
|
77
|
+
left = result[f]
|
|
78
|
+
right = other[f]
|
|
79
|
+
if left and right:
|
|
80
|
+
result[f] = [t for t in left if t in right]
|
|
81
|
+
elif right:
|
|
82
|
+
result[f] = list(right)
|
|
83
|
+
# if only left has values, keep them as-is
|
|
84
|
+
|
|
85
|
+
# Highest version
|
|
86
|
+
if other["version"] > result["version"]:
|
|
87
|
+
result["version"] = other["version"]
|
|
88
|
+
|
|
89
|
+
# Safety thresholds – most restrictive
|
|
90
|
+
result["confidence_threshold"] = max(
|
|
91
|
+
result["confidence_threshold"], other["confidence_threshold"]
|
|
92
|
+
)
|
|
93
|
+
result["drift_threshold"] = min(
|
|
94
|
+
result["drift_threshold"], other["drift_threshold"]
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# Checkpoint frequency – more frequent wins
|
|
98
|
+
result["checkpoint_frequency"] = min(
|
|
99
|
+
result["checkpoint_frequency"], other["checkpoint_frequency"]
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Backpressure threshold – lower is more restrictive
|
|
103
|
+
result["backpressure_threshold"] = min(
|
|
104
|
+
result["backpressure_threshold"], other["backpressure_threshold"]
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
return GovernancePolicy(**result)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class PolicyHierarchy:
|
|
111
|
+
"""Tree-structured policy inheritance.
|
|
112
|
+
|
|
113
|
+
A hierarchy node wraps a *base* (parent) policy and can produce child
|
|
114
|
+
policies that inherit from it, with optional overrides.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
def __init__(self, base: GovernancePolicy) -> None:
|
|
118
|
+
self._base = base
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def policy(self) -> GovernancePolicy:
|
|
122
|
+
"""The base policy of this hierarchy node."""
|
|
123
|
+
return self._base
|
|
124
|
+
|
|
125
|
+
def extend(self, **overrides: Any) -> GovernancePolicy:
|
|
126
|
+
"""Return a new policy that inherits from the base with *overrides*."""
|
|
127
|
+
return override_policy(self._base, **overrides)
|
|
128
|
+
|
|
129
|
+
def child(self, name: str, **overrides: Any) -> PolicyHierarchy:
|
|
130
|
+
"""Create a child hierarchy node inheriting from this one."""
|
|
131
|
+
child_policy = self.extend(name=name, **overrides)
|
|
132
|
+
return PolicyHierarchy(child_policy)
|
|
133
|
+
|
|
134
|
+
def chain(self, *policies: GovernancePolicy) -> GovernancePolicy:
|
|
135
|
+
"""Apply policies in priority order on top of the base.
|
|
136
|
+
|
|
137
|
+
Later policies win for simple scalar fields; list fields are unioned.
|
|
138
|
+
"""
|
|
139
|
+
if not policies:
|
|
140
|
+
return override_policy(self._base)
|
|
141
|
+
|
|
142
|
+
result = asdict(self._base)
|
|
143
|
+
|
|
144
|
+
for policy in policies:
|
|
145
|
+
other = asdict(policy)
|
|
146
|
+
for f in fields(GovernancePolicy):
|
|
147
|
+
key = f.name
|
|
148
|
+
val = other[key]
|
|
149
|
+
if key == "name":
|
|
150
|
+
continue # handled separately
|
|
151
|
+
if key in _UNION_LIST_FIELDS:
|
|
152
|
+
combined = list(result[key])
|
|
153
|
+
for item in val:
|
|
154
|
+
if item not in combined:
|
|
155
|
+
combined.append(item)
|
|
156
|
+
result[key] = combined
|
|
157
|
+
elif key in _INTERSECT_LIST_FIELDS:
|
|
158
|
+
left = result[key]
|
|
159
|
+
right = val
|
|
160
|
+
if left and right:
|
|
161
|
+
result[key] = [t for t in left if t in right]
|
|
162
|
+
elif right:
|
|
163
|
+
result[key] = list(right)
|
|
164
|
+
else:
|
|
165
|
+
result[key] = val
|
|
166
|
+
|
|
167
|
+
# Build composite name
|
|
168
|
+
names = [self._base.name] + [p.name for p in policies]
|
|
169
|
+
result["name"] = " + ".join(names)
|
|
170
|
+
|
|
171
|
+
return GovernancePolicy(**result)
|