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,629 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""
|
|
4
|
+
HuggingFace smolagents Integration for Agent-OS
|
|
5
|
+
================================================
|
|
6
|
+
|
|
7
|
+
Provides kernel-level governance for smolagents agent workflows.
|
|
8
|
+
|
|
9
|
+
Features:
|
|
10
|
+
- Extends BaseIntegration with wrap/unwrap for smolagents agents
|
|
11
|
+
- Policy enforcement via tool-call interception
|
|
12
|
+
- Tool allow/block lists
|
|
13
|
+
- Content filtering with blocked patterns
|
|
14
|
+
- Human approval workflow for sensitive tools
|
|
15
|
+
- Token/call budget tracking
|
|
16
|
+
- Full audit trail of tool calls and agent runs
|
|
17
|
+
- Works without smolagents installed (graceful import handling)
|
|
18
|
+
- Compatible with CodeAgent and ToolCallingAgent
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
>>> from agent_os.integrations.smolagents_adapter import SmolagentsKernel
|
|
22
|
+
>>>
|
|
23
|
+
>>> kernel = SmolagentsKernel(
|
|
24
|
+
... max_tool_calls=10,
|
|
25
|
+
... blocked_tools=["exec_code", "shell"],
|
|
26
|
+
... blocked_patterns=["DROP TABLE", "rm -rf"],
|
|
27
|
+
... require_human_approval=True,
|
|
28
|
+
... sensitive_tools=["delete_file", "send_email"],
|
|
29
|
+
... )
|
|
30
|
+
>>>
|
|
31
|
+
>>> # Wrap an existing agent
|
|
32
|
+
>>> from smolagents import CodeAgent, HfApiModel
|
|
33
|
+
>>> agent = CodeAgent(tools=[my_tool], model=HfApiModel())
|
|
34
|
+
>>> governed = kernel.wrap(agent)
|
|
35
|
+
>>> result = governed.run("Summarize this document")
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
from __future__ import annotations
|
|
39
|
+
|
|
40
|
+
import logging
|
|
41
|
+
import time
|
|
42
|
+
from dataclasses import dataclass, field
|
|
43
|
+
from typing import Any, Callable
|
|
44
|
+
|
|
45
|
+
from .base import BaseIntegration, GovernancePolicy
|
|
46
|
+
|
|
47
|
+
logger = logging.getLogger(__name__)
|
|
48
|
+
|
|
49
|
+
# Graceful import of smolagents
|
|
50
|
+
try:
|
|
51
|
+
import smolagents as _smolagents # noqa: F401
|
|
52
|
+
|
|
53
|
+
_HAS_SMOLAGENTS = True
|
|
54
|
+
except ImportError:
|
|
55
|
+
_HAS_SMOLAGENTS = False
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _check_smolagents_available() -> None:
|
|
59
|
+
"""Raise a helpful error when the ``smolagents`` package is missing."""
|
|
60
|
+
if not _HAS_SMOLAGENTS:
|
|
61
|
+
raise ImportError(
|
|
62
|
+
"The 'smolagents' package is required for live smolagents agent wrapping. "
|
|
63
|
+
"Install it with: pip install smolagents"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass
|
|
68
|
+
class PolicyConfig:
|
|
69
|
+
"""Policy configuration for smolagents governance."""
|
|
70
|
+
|
|
71
|
+
max_tool_calls: int = 50
|
|
72
|
+
max_agent_calls: int = 20
|
|
73
|
+
timeout_seconds: int = 300
|
|
74
|
+
|
|
75
|
+
allowed_tools: list[str] = field(default_factory=list)
|
|
76
|
+
blocked_tools: list[str] = field(default_factory=list)
|
|
77
|
+
|
|
78
|
+
blocked_patterns: list[str] = field(default_factory=list)
|
|
79
|
+
|
|
80
|
+
log_all_calls: bool = True
|
|
81
|
+
|
|
82
|
+
require_human_approval: bool = False
|
|
83
|
+
sensitive_tools: list[str] = field(default_factory=list)
|
|
84
|
+
|
|
85
|
+
max_budget: float | None = None
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class PolicyViolationError(Exception):
|
|
89
|
+
"""Raised when a governance policy is violated."""
|
|
90
|
+
|
|
91
|
+
def __init__(self, policy_name: str, description: str, severity: str = "high"):
|
|
92
|
+
self.policy_name = policy_name
|
|
93
|
+
self.description = description
|
|
94
|
+
self.severity = severity
|
|
95
|
+
super().__init__(f"Policy violation ({policy_name}): {description}")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@dataclass
|
|
99
|
+
class AuditEvent:
|
|
100
|
+
"""Single audit trail entry."""
|
|
101
|
+
|
|
102
|
+
timestamp: float
|
|
103
|
+
event_type: str
|
|
104
|
+
agent_name: str
|
|
105
|
+
details: dict[str, Any]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class SmolagentsKernel(BaseIntegration):
|
|
109
|
+
"""
|
|
110
|
+
Governance kernel for HuggingFace smolagents.
|
|
111
|
+
|
|
112
|
+
Extends BaseIntegration and intercepts tool calls on smolagents
|
|
113
|
+
CodeAgent and ToolCallingAgent instances by wrapping each tool's
|
|
114
|
+
``forward`` method with governance checks.
|
|
115
|
+
|
|
116
|
+
Supports human approval workflows for sensitive tools and
|
|
117
|
+
token/call budget tracking.
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
def __init__(
|
|
121
|
+
self,
|
|
122
|
+
policy: PolicyConfig | None = None,
|
|
123
|
+
on_violation: Callable[[PolicyViolationError], None] | None = None,
|
|
124
|
+
*,
|
|
125
|
+
# Convenience kwargs (create PolicyConfig automatically)
|
|
126
|
+
max_tool_calls: int = 50,
|
|
127
|
+
max_agent_calls: int = 20,
|
|
128
|
+
timeout_seconds: int = 300,
|
|
129
|
+
allowed_tools: list[str] | None = None,
|
|
130
|
+
blocked_tools: list[str] | None = None,
|
|
131
|
+
blocked_patterns: list[str] | None = None,
|
|
132
|
+
require_human_approval: bool = False,
|
|
133
|
+
sensitive_tools: list[str] | None = None,
|
|
134
|
+
max_budget: float | None = None,
|
|
135
|
+
):
|
|
136
|
+
if policy is not None:
|
|
137
|
+
self._sm_config = policy
|
|
138
|
+
else:
|
|
139
|
+
self._sm_config = PolicyConfig(
|
|
140
|
+
max_tool_calls=max_tool_calls,
|
|
141
|
+
max_agent_calls=max_agent_calls,
|
|
142
|
+
timeout_seconds=timeout_seconds,
|
|
143
|
+
allowed_tools=allowed_tools or [],
|
|
144
|
+
blocked_tools=blocked_tools or [],
|
|
145
|
+
blocked_patterns=blocked_patterns or [],
|
|
146
|
+
require_human_approval=require_human_approval,
|
|
147
|
+
sensitive_tools=sensitive_tools or [],
|
|
148
|
+
max_budget=max_budget,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Initialize BaseIntegration with a GovernancePolicy mapped from PolicyConfig
|
|
152
|
+
governance_policy = GovernancePolicy(
|
|
153
|
+
max_tool_calls=self._sm_config.max_tool_calls,
|
|
154
|
+
timeout_seconds=self._sm_config.timeout_seconds,
|
|
155
|
+
allowed_tools=list(self._sm_config.allowed_tools),
|
|
156
|
+
blocked_patterns=list(self._sm_config.blocked_patterns),
|
|
157
|
+
require_human_approval=self._sm_config.require_human_approval,
|
|
158
|
+
log_all_calls=self._sm_config.log_all_calls,
|
|
159
|
+
)
|
|
160
|
+
super().__init__(policy=governance_policy)
|
|
161
|
+
|
|
162
|
+
self.on_violation = on_violation or self._default_violation_handler
|
|
163
|
+
|
|
164
|
+
# Counters
|
|
165
|
+
self._tool_call_count: int = 0
|
|
166
|
+
self._agent_call_count: int = 0
|
|
167
|
+
self._start_time: float = time.time()
|
|
168
|
+
self._budget_spent: float = 0.0
|
|
169
|
+
|
|
170
|
+
# Audit trail
|
|
171
|
+
self._audit_log: list[AuditEvent] = []
|
|
172
|
+
|
|
173
|
+
# Violations collected
|
|
174
|
+
self._violations: list[PolicyViolationError] = []
|
|
175
|
+
|
|
176
|
+
# Human approval tracking
|
|
177
|
+
self._pending_approvals: dict[str, dict[str, Any]] = {}
|
|
178
|
+
self._approved_calls: dict[str, bool] = {}
|
|
179
|
+
|
|
180
|
+
# Wrapped agents registry and original forward methods
|
|
181
|
+
self._wrapped_agents: dict[str, Any] = {}
|
|
182
|
+
self._original_forwards: dict[str, Callable[..., Any]] = {}
|
|
183
|
+
|
|
184
|
+
# ------------------------------------------------------------------
|
|
185
|
+
# BaseIntegration abstract methods
|
|
186
|
+
# ------------------------------------------------------------------
|
|
187
|
+
|
|
188
|
+
def wrap(self, agent: Any) -> Any:
|
|
189
|
+
"""
|
|
190
|
+
Wrap a smolagents agent with governance.
|
|
191
|
+
|
|
192
|
+
Intercepts each tool's ``forward`` method so that every tool call
|
|
193
|
+
passes through policy checks before execution. The agent's
|
|
194
|
+
``toolbox`` (dict of tool-name → Tool) is iterated and each tool
|
|
195
|
+
is wrapped in-place.
|
|
196
|
+
|
|
197
|
+
Works without smolagents installed (for testing with mocks).
|
|
198
|
+
"""
|
|
199
|
+
agent_name = getattr(agent, "name", None) or str(id(agent))
|
|
200
|
+
|
|
201
|
+
# smolagents stores tools in agent.toolbox (dict-like or has .tools)
|
|
202
|
+
tools = self._get_tools(agent)
|
|
203
|
+
for tool_name, tool_obj in tools.items():
|
|
204
|
+
self._wrap_tool(tool_obj, tool_name, agent_name)
|
|
205
|
+
|
|
206
|
+
self._wrapped_agents[agent_name] = agent
|
|
207
|
+
self._record("agent_wrapped", agent_name, {"agent_type": type(agent).__name__})
|
|
208
|
+
logger.info("Wrapped smolagents agent '%s' with governance kernel", agent_name)
|
|
209
|
+
return agent
|
|
210
|
+
|
|
211
|
+
def unwrap(self, governed_agent: Any) -> Any:
|
|
212
|
+
"""Remove governance wrapper and restore original tool forwards."""
|
|
213
|
+
agent_name = getattr(governed_agent, "name", None) or str(id(governed_agent))
|
|
214
|
+
|
|
215
|
+
tools = self._get_tools(governed_agent)
|
|
216
|
+
for tool_name, tool_obj in tools.items():
|
|
217
|
+
key = f"{agent_name}:{tool_name}"
|
|
218
|
+
if key in self._original_forwards:
|
|
219
|
+
tool_obj.forward = self._original_forwards.pop(key)
|
|
220
|
+
|
|
221
|
+
self._wrapped_agents.pop(agent_name, None)
|
|
222
|
+
return governed_agent
|
|
223
|
+
|
|
224
|
+
# ------------------------------------------------------------------
|
|
225
|
+
# Tool wrapping
|
|
226
|
+
# ------------------------------------------------------------------
|
|
227
|
+
|
|
228
|
+
@staticmethod
|
|
229
|
+
def _get_tools(agent: Any) -> dict[str, Any]:
|
|
230
|
+
"""Extract the tool dict from a smolagents agent.
|
|
231
|
+
|
|
232
|
+
smolagents agents expose tools via ``agent.toolbox`` which may be
|
|
233
|
+
a ``Toolbox`` object (with a ``.tools`` dict) or a plain dict.
|
|
234
|
+
Falls back to an empty dict when no toolbox is found.
|
|
235
|
+
"""
|
|
236
|
+
toolbox = getattr(agent, "toolbox", None)
|
|
237
|
+
if toolbox is None:
|
|
238
|
+
return {}
|
|
239
|
+
# Toolbox object has a .tools dict
|
|
240
|
+
if hasattr(toolbox, "tools"):
|
|
241
|
+
return toolbox.tools
|
|
242
|
+
# Plain dict
|
|
243
|
+
if isinstance(toolbox, dict):
|
|
244
|
+
return toolbox
|
|
245
|
+
return {}
|
|
246
|
+
|
|
247
|
+
def _wrap_tool(self, tool: Any, tool_name: str, agent_name: str) -> None:
|
|
248
|
+
"""Replace ``tool.forward`` with a governed version."""
|
|
249
|
+
original_forward = tool.forward
|
|
250
|
+
key = f"{agent_name}:{tool_name}"
|
|
251
|
+
self._original_forwards[key] = original_forward
|
|
252
|
+
|
|
253
|
+
kernel = self
|
|
254
|
+
|
|
255
|
+
def governed_forward(*args: Any, **kwargs: Any) -> Any:
|
|
256
|
+
"""Governed wrapper around a smolagents tool's forward method.
|
|
257
|
+
|
|
258
|
+
Intercepts the tool invocation, validates the call against
|
|
259
|
+
the active policy, updates call counters and the audit log,
|
|
260
|
+
then delegates to the original forward implementation.
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
*args: Positional arguments forwarded to the original tool.
|
|
264
|
+
**kwargs: Keyword arguments forwarded to the original tool.
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
The result from the original tool's forward method.
|
|
268
|
+
|
|
269
|
+
Raises:
|
|
270
|
+
PolicyViolationError: If the call violates the active policy.
|
|
271
|
+
"""
|
|
272
|
+
# Pre-execution governance check
|
|
273
|
+
result = kernel.before_tool_call(
|
|
274
|
+
tool_name=tool_name,
|
|
275
|
+
tool_args=kwargs or (args[0] if args else {}),
|
|
276
|
+
agent_name=agent_name,
|
|
277
|
+
)
|
|
278
|
+
if result is not None:
|
|
279
|
+
raise PolicyViolationError(
|
|
280
|
+
result.get("policy", "governance"),
|
|
281
|
+
result.get("error", "Tool call blocked by policy"),
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
# Execute original tool
|
|
285
|
+
output = original_forward(*args, **kwargs)
|
|
286
|
+
|
|
287
|
+
# Post-execution governance check
|
|
288
|
+
filtered = kernel.after_tool_call(
|
|
289
|
+
tool_name=tool_name,
|
|
290
|
+
tool_result=output,
|
|
291
|
+
agent_name=agent_name,
|
|
292
|
+
)
|
|
293
|
+
return filtered
|
|
294
|
+
|
|
295
|
+
tool.forward = governed_forward
|
|
296
|
+
|
|
297
|
+
# ------------------------------------------------------------------
|
|
298
|
+
# Internal helpers
|
|
299
|
+
# ------------------------------------------------------------------
|
|
300
|
+
|
|
301
|
+
def _default_violation_handler(self, error: PolicyViolationError) -> None:
|
|
302
|
+
"""Default handler called when a policy violation occurs.
|
|
303
|
+
|
|
304
|
+
Logs the violation as an error. Override by passing a custom
|
|
305
|
+
on_violation callback to the kernel constructor.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
error: The PolicyViolationError that was raised.
|
|
309
|
+
"""
|
|
310
|
+
logger.error(f"Policy violation: {error}")
|
|
311
|
+
|
|
312
|
+
def _record(self, event_type: str, agent_name: str, details: dict[str, Any]) -> None:
|
|
313
|
+
"""Append an audit event to the internal audit log.
|
|
314
|
+
|
|
315
|
+
Records the event only when log_all_calls is enabled.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
event_type: Short string label for the event.
|
|
319
|
+
agent_name: ID or name of the agent generating the event.
|
|
320
|
+
details: Arbitrary dict of additional context.
|
|
321
|
+
"""
|
|
322
|
+
if self._sm_config.log_all_calls:
|
|
323
|
+
self._audit_log.append(
|
|
324
|
+
AuditEvent(
|
|
325
|
+
timestamp=time.time(),
|
|
326
|
+
event_type=event_type,
|
|
327
|
+
agent_name=agent_name,
|
|
328
|
+
details=details,
|
|
329
|
+
)
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
def _check_tool_allowed(self, tool_name: str) -> tuple[bool, str]:
|
|
333
|
+
"""Check whether a tool is permitted by the active policy.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
tool_name: Name of the tool to check.
|
|
337
|
+
|
|
338
|
+
Returns:
|
|
339
|
+
Tuple of (allowed: bool, reason: str).
|
|
340
|
+
"""
|
|
341
|
+
if tool_name in self._sm_config.blocked_tools:
|
|
342
|
+
return False, f"Tool '{tool_name}' is blocked by policy"
|
|
343
|
+
if self._sm_config.allowed_tools and tool_name not in self._sm_config.allowed_tools:
|
|
344
|
+
return False, f"Tool '{tool_name}' not in allowed list"
|
|
345
|
+
return True, ""
|
|
346
|
+
|
|
347
|
+
def _check_content(self, content: str) -> tuple[bool, str]:
|
|
348
|
+
"""Scan a string for policy-blocked patterns.
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
content: The text to scan.
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
Tuple of (allowed: bool, reason: str).
|
|
355
|
+
"""
|
|
356
|
+
content_lower = content.lower()
|
|
357
|
+
for pattern in self._sm_config.blocked_patterns:
|
|
358
|
+
if pattern.lower() in content_lower:
|
|
359
|
+
return False, f"Content matches blocked pattern: '{pattern}'"
|
|
360
|
+
return True, ""
|
|
361
|
+
|
|
362
|
+
def _check_timeout(self) -> tuple[bool, str]:
|
|
363
|
+
"""Check whether the kernel has exceeded its configured timeout.
|
|
364
|
+
|
|
365
|
+
Returns:
|
|
366
|
+
Tuple of (within_limit: bool, reason: str).
|
|
367
|
+
"""
|
|
368
|
+
elapsed = time.time() - self._start_time
|
|
369
|
+
if elapsed > self._sm_config.timeout_seconds:
|
|
370
|
+
return False, f"Execution timeout ({elapsed:.0f}s > {self._sm_config.timeout_seconds}s)"
|
|
371
|
+
return True, ""
|
|
372
|
+
|
|
373
|
+
def _check_budget(self, cost: float = 1.0) -> tuple[bool, str]:
|
|
374
|
+
"""Check whether a tool call would exceed the configured cost budget.
|
|
375
|
+
|
|
376
|
+
Args:
|
|
377
|
+
cost: Cost units to add for this call (default 1.0).
|
|
378
|
+
|
|
379
|
+
Returns:
|
|
380
|
+
Tuple of (within_budget: bool, reason: str).
|
|
381
|
+
"""
|
|
382
|
+
if self._sm_config.max_budget is not None:
|
|
383
|
+
if self._budget_spent + cost > self._sm_config.max_budget:
|
|
384
|
+
return False, (
|
|
385
|
+
f"Budget exceeded: spent {self._budget_spent} + {cost} "
|
|
386
|
+
f"> limit {self._sm_config.max_budget}"
|
|
387
|
+
)
|
|
388
|
+
return True, ""
|
|
389
|
+
|
|
390
|
+
def _needs_approval(self, tool_name: str) -> bool:
|
|
391
|
+
"""Check if a tool call requires human approval."""
|
|
392
|
+
if not self._sm_config.require_human_approval:
|
|
393
|
+
return False
|
|
394
|
+
if self._sm_config.sensitive_tools:
|
|
395
|
+
return tool_name in self._sm_config.sensitive_tools
|
|
396
|
+
return True
|
|
397
|
+
|
|
398
|
+
def _raise_violation(self, policy_name: str, description: str) -> PolicyViolationError:
|
|
399
|
+
"""Create, record, and surface a PolicyViolationError.
|
|
400
|
+
|
|
401
|
+
Appends the error to the violations list and calls on_violation.
|
|
402
|
+
|
|
403
|
+
Args:
|
|
404
|
+
policy_name: Short identifier for the violated policy rule.
|
|
405
|
+
description: Human-readable description of the violation.
|
|
406
|
+
|
|
407
|
+
Returns:
|
|
408
|
+
The constructed PolicyViolationError (caller may raise it).
|
|
409
|
+
"""
|
|
410
|
+
error = PolicyViolationError(policy_name, description)
|
|
411
|
+
self._violations.append(error)
|
|
412
|
+
self.on_violation(error)
|
|
413
|
+
return error
|
|
414
|
+
|
|
415
|
+
# ------------------------------------------------------------------
|
|
416
|
+
# Tool-call governance hooks
|
|
417
|
+
# ------------------------------------------------------------------
|
|
418
|
+
|
|
419
|
+
def before_tool_call(
|
|
420
|
+
self,
|
|
421
|
+
tool_name: str = "unknown",
|
|
422
|
+
tool_args: Any = None,
|
|
423
|
+
agent_name: str = "unknown",
|
|
424
|
+
cost: float = 1.0,
|
|
425
|
+
) -> dict[str, Any] | None:
|
|
426
|
+
"""
|
|
427
|
+
Pre-execution governance check for a tool call.
|
|
428
|
+
|
|
429
|
+
Returns None to allow execution, or a dict with error info to block it.
|
|
430
|
+
"""
|
|
431
|
+
if tool_args is None:
|
|
432
|
+
tool_args = {}
|
|
433
|
+
|
|
434
|
+
self._record("before_tool", agent_name, {"tool": tool_name, "args": tool_args})
|
|
435
|
+
|
|
436
|
+
# Check timeout
|
|
437
|
+
ok, reason = self._check_timeout()
|
|
438
|
+
if not ok:
|
|
439
|
+
error = self._raise_violation("timeout", reason)
|
|
440
|
+
return {"error": str(error), "policy": "timeout"}
|
|
441
|
+
|
|
442
|
+
# Check tool count
|
|
443
|
+
self._tool_call_count += 1
|
|
444
|
+
if self._tool_call_count > self._sm_config.max_tool_calls:
|
|
445
|
+
error = self._raise_violation(
|
|
446
|
+
"tool_limit",
|
|
447
|
+
f"Tool call count ({self._tool_call_count}) exceeds limit ({self._sm_config.max_tool_calls})",
|
|
448
|
+
)
|
|
449
|
+
return {"error": str(error), "policy": "tool_limit"}
|
|
450
|
+
|
|
451
|
+
# Check budget
|
|
452
|
+
ok, reason = self._check_budget(cost)
|
|
453
|
+
if not ok:
|
|
454
|
+
error = self._raise_violation("budget_exceeded", reason)
|
|
455
|
+
return {"error": str(error), "policy": "budget_exceeded"}
|
|
456
|
+
|
|
457
|
+
# Check tool allowed
|
|
458
|
+
ok, reason = self._check_tool_allowed(tool_name)
|
|
459
|
+
if not ok:
|
|
460
|
+
error = self._raise_violation("tool_filter", reason)
|
|
461
|
+
return {"error": str(error), "policy": "tool_filter"}
|
|
462
|
+
|
|
463
|
+
# Check content in arguments
|
|
464
|
+
if isinstance(tool_args, dict):
|
|
465
|
+
for value in tool_args.values():
|
|
466
|
+
if isinstance(value, str):
|
|
467
|
+
ok, reason = self._check_content(value)
|
|
468
|
+
if not ok:
|
|
469
|
+
error = self._raise_violation("content_filter", reason)
|
|
470
|
+
return {"error": str(error), "policy": "content_filter"}
|
|
471
|
+
elif isinstance(tool_args, str):
|
|
472
|
+
ok, reason = self._check_content(tool_args)
|
|
473
|
+
if not ok:
|
|
474
|
+
error = self._raise_violation("content_filter", reason)
|
|
475
|
+
return {"error": str(error), "policy": "content_filter"}
|
|
476
|
+
|
|
477
|
+
# Human approval check
|
|
478
|
+
if self._needs_approval(tool_name):
|
|
479
|
+
call_id = f"{agent_name}:{tool_name}:{self._tool_call_count}"
|
|
480
|
+
if call_id not in self._approved_calls:
|
|
481
|
+
self._pending_approvals[call_id] = {
|
|
482
|
+
"tool_name": tool_name,
|
|
483
|
+
"tool_args": tool_args,
|
|
484
|
+
"agent_name": agent_name,
|
|
485
|
+
"timestamp": time.time(),
|
|
486
|
+
}
|
|
487
|
+
self._record("approval_required", agent_name, {
|
|
488
|
+
"tool": tool_name, "call_id": call_id,
|
|
489
|
+
})
|
|
490
|
+
error = self._raise_violation(
|
|
491
|
+
"human_approval_required",
|
|
492
|
+
f"Tool '{tool_name}' requires human approval (call_id={call_id})",
|
|
493
|
+
)
|
|
494
|
+
return {
|
|
495
|
+
"error": str(error),
|
|
496
|
+
"call_id": call_id,
|
|
497
|
+
"needs_approval": True,
|
|
498
|
+
"policy": "human_approval_required",
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
# Track budget spend
|
|
502
|
+
self._budget_spent += cost
|
|
503
|
+
|
|
504
|
+
return None # Allow execution
|
|
505
|
+
|
|
506
|
+
def after_tool_call(
|
|
507
|
+
self,
|
|
508
|
+
tool_name: str = "unknown",
|
|
509
|
+
tool_result: Any = None,
|
|
510
|
+
agent_name: str = "unknown",
|
|
511
|
+
) -> Any:
|
|
512
|
+
"""
|
|
513
|
+
Post-execution governance check for a tool call.
|
|
514
|
+
|
|
515
|
+
Inspects tool output for blocked patterns.
|
|
516
|
+
Returns the (possibly modified) tool_result, or raises on violation.
|
|
517
|
+
"""
|
|
518
|
+
self._record("after_tool", agent_name, {
|
|
519
|
+
"tool": tool_name,
|
|
520
|
+
"result_type": type(tool_result).__name__,
|
|
521
|
+
})
|
|
522
|
+
|
|
523
|
+
if isinstance(tool_result, str):
|
|
524
|
+
ok, reason = self._check_content(tool_result)
|
|
525
|
+
if not ok:
|
|
526
|
+
self._raise_violation("output_filter", reason)
|
|
527
|
+
return f"[BLOCKED] {reason}"
|
|
528
|
+
|
|
529
|
+
if isinstance(tool_result, dict):
|
|
530
|
+
for value in tool_result.values():
|
|
531
|
+
if isinstance(value, str):
|
|
532
|
+
ok, reason = self._check_content(value)
|
|
533
|
+
if not ok:
|
|
534
|
+
self._raise_violation("output_filter", reason)
|
|
535
|
+
return {"error": reason}
|
|
536
|
+
|
|
537
|
+
return tool_result
|
|
538
|
+
|
|
539
|
+
# ------------------------------------------------------------------
|
|
540
|
+
# Human Approval API
|
|
541
|
+
# ------------------------------------------------------------------
|
|
542
|
+
|
|
543
|
+
def approve(self, call_id: str) -> bool:
|
|
544
|
+
"""Approve a pending tool call by its call_id."""
|
|
545
|
+
if call_id in self._pending_approvals:
|
|
546
|
+
self._approved_calls[call_id] = True
|
|
547
|
+
info = self._pending_approvals.pop(call_id)
|
|
548
|
+
self._record("approval_granted", info.get("agent_name", "unknown"), {
|
|
549
|
+
"call_id": call_id, "tool": info.get("tool_name"),
|
|
550
|
+
})
|
|
551
|
+
return True
|
|
552
|
+
return False
|
|
553
|
+
|
|
554
|
+
def deny(self, call_id: str) -> bool:
|
|
555
|
+
"""Deny a pending tool call by its call_id."""
|
|
556
|
+
if call_id in self._pending_approvals:
|
|
557
|
+
info = self._pending_approvals.pop(call_id)
|
|
558
|
+
self._record("approval_denied", info.get("agent_name", "unknown"), {
|
|
559
|
+
"call_id": call_id, "tool": info.get("tool_name"),
|
|
560
|
+
})
|
|
561
|
+
return True
|
|
562
|
+
return False
|
|
563
|
+
|
|
564
|
+
def get_pending_approvals(self) -> dict[str, dict[str, Any]]:
|
|
565
|
+
"""Return all pending approval requests."""
|
|
566
|
+
return dict(self._pending_approvals)
|
|
567
|
+
|
|
568
|
+
# ------------------------------------------------------------------
|
|
569
|
+
# Public API
|
|
570
|
+
# ------------------------------------------------------------------
|
|
571
|
+
|
|
572
|
+
def reset(self) -> None:
|
|
573
|
+
"""Reset counters and start time (for new execution runs)."""
|
|
574
|
+
self._tool_call_count = 0
|
|
575
|
+
self._agent_call_count = 0
|
|
576
|
+
self._start_time = time.time()
|
|
577
|
+
self._budget_spent = 0.0
|
|
578
|
+
|
|
579
|
+
def get_audit_log(self) -> list[AuditEvent]:
|
|
580
|
+
"""Return the full audit trail."""
|
|
581
|
+
return list(self._audit_log)
|
|
582
|
+
|
|
583
|
+
def get_violations(self) -> list[PolicyViolationError]:
|
|
584
|
+
"""Return all collected violations."""
|
|
585
|
+
return list(self._violations)
|
|
586
|
+
|
|
587
|
+
def get_stats(self) -> dict[str, Any]:
|
|
588
|
+
"""Get governance statistics."""
|
|
589
|
+
return {
|
|
590
|
+
"tool_calls": self._tool_call_count,
|
|
591
|
+
"agent_calls": self._agent_call_count,
|
|
592
|
+
"violations": len(self._violations),
|
|
593
|
+
"audit_events": len(self._audit_log),
|
|
594
|
+
"elapsed_seconds": round(time.time() - self._start_time, 2),
|
|
595
|
+
"budget_spent": self._budget_spent,
|
|
596
|
+
"budget_limit": self._sm_config.max_budget,
|
|
597
|
+
"pending_approvals": len(self._pending_approvals),
|
|
598
|
+
"policy": {
|
|
599
|
+
"max_tool_calls": self._sm_config.max_tool_calls,
|
|
600
|
+
"max_agent_calls": self._sm_config.max_agent_calls,
|
|
601
|
+
"blocked_tools": self._sm_config.blocked_tools,
|
|
602
|
+
"allowed_tools": self._sm_config.allowed_tools,
|
|
603
|
+
"require_human_approval": self._sm_config.require_human_approval,
|
|
604
|
+
"sensitive_tools": self._sm_config.sensitive_tools,
|
|
605
|
+
},
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
def health_check(self) -> dict[str, Any]:
|
|
609
|
+
"""Return adapter health status."""
|
|
610
|
+
elapsed = time.time() - self._start_time
|
|
611
|
+
has_violations = len(self._violations) > 0
|
|
612
|
+
return {
|
|
613
|
+
"status": "degraded" if has_violations else "healthy",
|
|
614
|
+
"backend": "smolagents",
|
|
615
|
+
"smolagents_available": _HAS_SMOLAGENTS,
|
|
616
|
+
"wrapped_agents": len(self._wrapped_agents),
|
|
617
|
+
"violations": len(self._violations),
|
|
618
|
+
"uptime_seconds": round(elapsed, 2),
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
__all__ = [
|
|
623
|
+
"SmolagentsKernel",
|
|
624
|
+
"PolicyConfig",
|
|
625
|
+
"PolicyViolationError",
|
|
626
|
+
"AuditEvent",
|
|
627
|
+
"_HAS_SMOLAGENTS",
|
|
628
|
+
"_check_smolagents_available",
|
|
629
|
+
]
|