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,422 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
The Mute Agent - Scale by Subtraction
|
|
6
|
+
|
|
7
|
+
The Mute Agent represents the philosophy of "Scale by Subtraction" - removing
|
|
8
|
+
creativity and ensuring agents fail fast with NULL responses when requests
|
|
9
|
+
are out of scope, rather than hallucinating or being conversational.
|
|
10
|
+
|
|
11
|
+
This module provides capabilities for agents to strictly operate within
|
|
12
|
+
their defined constraints and return NULL/silence for out-of-scope requests.
|
|
13
|
+
|
|
14
|
+
Research Foundations:
|
|
15
|
+
- Capability-based security model (principle of least privilege)
|
|
16
|
+
- "If a system can't do something, it can't be tricked into doing it"
|
|
17
|
+
- Fail-safe defaults from secure system design
|
|
18
|
+
- Inspired by NULL semantics in type systems and databases
|
|
19
|
+
|
|
20
|
+
See docs/RESEARCH_FOUNDATION.md for complete references.
|
|
21
|
+
|
|
22
|
+
DEPRECATION NOTICE:
|
|
23
|
+
This module is being refactored to use the plugin interface pattern.
|
|
24
|
+
New code should use CapabilityValidatorInterface from
|
|
25
|
+
agent_control_plane.interfaces instead of directly importing this module.
|
|
26
|
+
|
|
27
|
+
The MuteAgentValidator class now implements CapabilityValidatorInterface
|
|
28
|
+
and can be registered with the PluginRegistry for dependency injection.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
import warnings
|
|
32
|
+
from typing import Any, Dict, Optional, List, Callable, Tuple
|
|
33
|
+
from dataclasses import dataclass, field
|
|
34
|
+
from datetime import datetime
|
|
35
|
+
from .agent_kernel import ActionType, ExecutionRequest
|
|
36
|
+
from .interfaces.plugin_interface import (
|
|
37
|
+
CapabilityValidatorInterface,
|
|
38
|
+
ValidationResult,
|
|
39
|
+
PluginMetadata,
|
|
40
|
+
PluginCapability,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class AgentCapability:
|
|
46
|
+
"""Defines a specific capability an agent has"""
|
|
47
|
+
name: str
|
|
48
|
+
description: str
|
|
49
|
+
action_types: List[ActionType]
|
|
50
|
+
parameter_schema: Dict[str, Any] # JSON schema for parameters
|
|
51
|
+
validator: Optional[Callable[[ExecutionRequest], bool]] = None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass
|
|
55
|
+
class MuteAgentConfig:
|
|
56
|
+
"""Configuration for a Mute Agent"""
|
|
57
|
+
agent_id: str
|
|
58
|
+
capabilities: List[AgentCapability] = field(default_factory=list)
|
|
59
|
+
strict_mode: bool = True # If True, reject anything outside capabilities
|
|
60
|
+
null_response_message: str = "NULL" # Response for out-of-scope requests
|
|
61
|
+
enable_explanation: bool = False # If True, explain why request was rejected
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class MuteAgentValidator(CapabilityValidatorInterface):
|
|
65
|
+
"""
|
|
66
|
+
Validates requests against agent capabilities.
|
|
67
|
+
|
|
68
|
+
The Mute Agent knows when to shut up. If a request doesn't map to a
|
|
69
|
+
defined capability, it returns NULL instead of hallucinating or trying
|
|
70
|
+
to be helpful in creative ways.
|
|
71
|
+
|
|
72
|
+
This class implements CapabilityValidatorInterface for use with the
|
|
73
|
+
PluginRegistry and dependency injection pattern.
|
|
74
|
+
|
|
75
|
+
DEPRECATION NOTICE:
|
|
76
|
+
Direct instantiation of MuteAgentValidator is deprecated.
|
|
77
|
+
Instead, use the PluginRegistry to register validators:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from agent_control_plane import PluginRegistry
|
|
81
|
+
|
|
82
|
+
registry = PluginRegistry()
|
|
83
|
+
validator = MuteAgentValidator(config)
|
|
84
|
+
registry.register_validator(validator)
|
|
85
|
+
```
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
def __init__(self, config: MuteAgentConfig):
|
|
89
|
+
self.config = config
|
|
90
|
+
self.rejection_log: List[Dict[str, Any]] = []
|
|
91
|
+
self._agent_capabilities: Dict[str, List[AgentCapability]] = {
|
|
92
|
+
config.agent_id: config.capabilities
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def metadata(self) -> PluginMetadata:
|
|
97
|
+
"""Return metadata about this validator plugin"""
|
|
98
|
+
return PluginMetadata(
|
|
99
|
+
name=f"mute_agent_validator_{self.config.agent_id}",
|
|
100
|
+
version="1.1.0",
|
|
101
|
+
description="Capability-based validator implementing Scale by Subtraction philosophy",
|
|
102
|
+
plugin_type="validator",
|
|
103
|
+
capabilities=[
|
|
104
|
+
PluginCapability.REQUEST_VALIDATION,
|
|
105
|
+
PluginCapability.CAPABILITY_CHECKING,
|
|
106
|
+
PluginCapability.PARAMETER_VALIDATION,
|
|
107
|
+
]
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
def validate_request(
|
|
111
|
+
self,
|
|
112
|
+
request: Any,
|
|
113
|
+
context: Optional[Dict[str, Any]] = None
|
|
114
|
+
) -> ValidationResult:
|
|
115
|
+
"""
|
|
116
|
+
Validate if request maps to a defined capability.
|
|
117
|
+
|
|
118
|
+
Implements CapabilityValidatorInterface.validate_request()
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
request: ExecutionRequest to validate
|
|
122
|
+
context: Optional additional context
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
ValidationResult with approval/denial and details
|
|
126
|
+
"""
|
|
127
|
+
# Support both ExecutionRequest and dict-based requests
|
|
128
|
+
if hasattr(request, 'action_type'):
|
|
129
|
+
action_type = request.action_type
|
|
130
|
+
request_id = getattr(request, 'request_id', 'unknown')
|
|
131
|
+
timestamp = getattr(request, 'timestamp', datetime.now())
|
|
132
|
+
else:
|
|
133
|
+
action_type = request.get('action_type')
|
|
134
|
+
request_id = request.get('request_id', 'unknown')
|
|
135
|
+
timestamp = request.get('timestamp', datetime.now())
|
|
136
|
+
|
|
137
|
+
# Check if action type is within any capability
|
|
138
|
+
matching_capabilities = [
|
|
139
|
+
cap for cap in self.config.capabilities
|
|
140
|
+
if action_type in cap.action_types
|
|
141
|
+
]
|
|
142
|
+
|
|
143
|
+
if not matching_capabilities:
|
|
144
|
+
reason = self._format_rejection_reason(
|
|
145
|
+
action_type,
|
|
146
|
+
"Action type not in agent capabilities"
|
|
147
|
+
)
|
|
148
|
+
self._log_rejection(request_id, action_type, reason, timestamp)
|
|
149
|
+
return ValidationResult(
|
|
150
|
+
is_valid=False,
|
|
151
|
+
reason=reason,
|
|
152
|
+
details={"action_type": str(action_type), "available_capabilities": [c.name for c in self.config.capabilities]}
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# Validate parameters against capability schema
|
|
156
|
+
for capability in matching_capabilities:
|
|
157
|
+
if capability.validator and hasattr(request, 'action_type'):
|
|
158
|
+
if not capability.validator(request):
|
|
159
|
+
reason = self._format_rejection_reason(
|
|
160
|
+
action_type,
|
|
161
|
+
f"Parameters do not match capability: {capability.name}"
|
|
162
|
+
)
|
|
163
|
+
self._log_rejection(request_id, action_type, reason, timestamp)
|
|
164
|
+
return ValidationResult(
|
|
165
|
+
is_valid=False,
|
|
166
|
+
reason=reason,
|
|
167
|
+
details={"capability": capability.name}
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
return ValidationResult(is_valid=True)
|
|
171
|
+
|
|
172
|
+
# Legacy method for backward compatibility
|
|
173
|
+
def validate_request_legacy(self, request: ExecutionRequest) -> Tuple[bool, Optional[str]]:
|
|
174
|
+
"""
|
|
175
|
+
Legacy validation method for backward compatibility.
|
|
176
|
+
|
|
177
|
+
DEPRECATED: Use validate_request() instead which returns ValidationResult.
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
(is_valid, reason_if_invalid)
|
|
181
|
+
"""
|
|
182
|
+
warnings.warn(
|
|
183
|
+
"validate_request_legacy is deprecated, use validate_request() instead",
|
|
184
|
+
DeprecationWarning,
|
|
185
|
+
stacklevel=2
|
|
186
|
+
)
|
|
187
|
+
result = self.validate_request(request)
|
|
188
|
+
return result.is_valid, result.reason
|
|
189
|
+
|
|
190
|
+
def validate_action(
|
|
191
|
+
self,
|
|
192
|
+
action_type: ActionType,
|
|
193
|
+
parameters: Dict[str, Any]
|
|
194
|
+
) -> Tuple[bool, Optional[str]]:
|
|
195
|
+
"""
|
|
196
|
+
Lightweight validation without creating an ExecutionRequest.
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
(is_valid, reason_if_invalid)
|
|
200
|
+
"""
|
|
201
|
+
# Check if action type is within any capability
|
|
202
|
+
matching_capabilities = [
|
|
203
|
+
cap for cap in self.config.capabilities
|
|
204
|
+
if action_type in cap.action_types
|
|
205
|
+
]
|
|
206
|
+
|
|
207
|
+
if not matching_capabilities:
|
|
208
|
+
reason = self._format_rejection_reason(
|
|
209
|
+
action_type,
|
|
210
|
+
"Action type not in agent capabilities"
|
|
211
|
+
)
|
|
212
|
+
return False, reason
|
|
213
|
+
|
|
214
|
+
# Note: Cannot validate with validator since it expects ExecutionRequest
|
|
215
|
+
# This is a tradeoff for performance - full validation requires ExecutionRequest
|
|
216
|
+
return True, None
|
|
217
|
+
|
|
218
|
+
# =========================================================================
|
|
219
|
+
# CapabilityValidatorInterface implementation
|
|
220
|
+
# =========================================================================
|
|
221
|
+
|
|
222
|
+
def define_capability(
|
|
223
|
+
self,
|
|
224
|
+
agent_id: str,
|
|
225
|
+
capability_name: str,
|
|
226
|
+
allowed_actions: List[str],
|
|
227
|
+
parameter_schema: Dict[str, Any],
|
|
228
|
+
validator: Optional[Callable[[Any], bool]] = None
|
|
229
|
+
) -> None:
|
|
230
|
+
"""
|
|
231
|
+
Define a capability for an agent.
|
|
232
|
+
|
|
233
|
+
Implements CapabilityValidatorInterface.define_capability()
|
|
234
|
+
"""
|
|
235
|
+
# Convert string action types to ActionType enums
|
|
236
|
+
action_types = []
|
|
237
|
+
for action in allowed_actions:
|
|
238
|
+
try:
|
|
239
|
+
action_types.append(ActionType(action))
|
|
240
|
+
except ValueError:
|
|
241
|
+
# Skip unknown action types
|
|
242
|
+
pass
|
|
243
|
+
|
|
244
|
+
capability = AgentCapability(
|
|
245
|
+
name=capability_name,
|
|
246
|
+
description=f"Capability {capability_name} for agent {agent_id}",
|
|
247
|
+
action_types=action_types,
|
|
248
|
+
parameter_schema=parameter_schema,
|
|
249
|
+
validator=validator
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
if agent_id not in self._agent_capabilities:
|
|
253
|
+
self._agent_capabilities[agent_id] = []
|
|
254
|
+
self._agent_capabilities[agent_id].append(capability)
|
|
255
|
+
|
|
256
|
+
# Also add to config if this is for the configured agent
|
|
257
|
+
if agent_id == self.config.agent_id:
|
|
258
|
+
self.config.capabilities.append(capability)
|
|
259
|
+
|
|
260
|
+
def get_agent_capabilities(self, agent_id: str) -> List[Dict[str, Any]]:
|
|
261
|
+
"""Get all capabilities defined for an agent"""
|
|
262
|
+
capabilities = self._agent_capabilities.get(agent_id, [])
|
|
263
|
+
return [
|
|
264
|
+
{
|
|
265
|
+
"name": cap.name,
|
|
266
|
+
"description": cap.description,
|
|
267
|
+
"action_types": [at.value for at in cap.action_types],
|
|
268
|
+
"parameter_schema": cap.parameter_schema,
|
|
269
|
+
"has_validator": cap.validator is not None
|
|
270
|
+
}
|
|
271
|
+
for cap in capabilities
|
|
272
|
+
]
|
|
273
|
+
|
|
274
|
+
def get_null_response(self) -> str:
|
|
275
|
+
"""Get the NULL response message for out-of-scope requests"""
|
|
276
|
+
return self.config.null_response_message
|
|
277
|
+
|
|
278
|
+
def get_rejection_log(self, agent_id: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
279
|
+
"""Get log of rejected (out-of-scope) requests"""
|
|
280
|
+
if agent_id:
|
|
281
|
+
return [r for r in self.rejection_log if r.get("agent_id") == agent_id]
|
|
282
|
+
return self.rejection_log.copy()
|
|
283
|
+
|
|
284
|
+
# =========================================================================
|
|
285
|
+
# Internal methods
|
|
286
|
+
# =========================================================================
|
|
287
|
+
|
|
288
|
+
def _format_rejection_reason(self, action_type: ActionType, reason: str) -> str:
|
|
289
|
+
"""Format rejection reason based on agent configuration"""
|
|
290
|
+
if self.config.enable_explanation:
|
|
291
|
+
return f"Request rejected: {reason}. Available capabilities: {[c.name for c in self.config.capabilities]}"
|
|
292
|
+
else:
|
|
293
|
+
return self.config.null_response_message
|
|
294
|
+
|
|
295
|
+
def _log_rejection(self, request_id: str, action_type: ActionType, reason: str, timestamp: datetime):
|
|
296
|
+
"""Log rejected requests for analysis"""
|
|
297
|
+
self.rejection_log.append({
|
|
298
|
+
"request_id": request_id,
|
|
299
|
+
"agent_id": self.config.agent_id,
|
|
300
|
+
"action_type": action_type.value if hasattr(action_type, 'value') else str(action_type),
|
|
301
|
+
"reason": reason,
|
|
302
|
+
"timestamp": timestamp.isoformat() if hasattr(timestamp, 'isoformat') else str(timestamp)
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def create_sql_agent_capabilities() -> List[AgentCapability]:
|
|
307
|
+
"""
|
|
308
|
+
Example: Create capabilities for a SQL-generating agent
|
|
309
|
+
|
|
310
|
+
This agent can only query databases, not modify them.
|
|
311
|
+
If asked to "build a rocket ship", it returns NULL instead of hallucinating.
|
|
312
|
+
"""
|
|
313
|
+
|
|
314
|
+
def validate_sql_query(request: ExecutionRequest) -> bool:
|
|
315
|
+
"""Validate that SQL query is read-only"""
|
|
316
|
+
import re
|
|
317
|
+
query = request.parameters.get('query', '').upper()
|
|
318
|
+
# Only SELECT queries allowed
|
|
319
|
+
if not query.strip().startswith('SELECT'):
|
|
320
|
+
return False
|
|
321
|
+
# No destructive operations (use word boundaries to avoid false matches)
|
|
322
|
+
destructive = ['DROP', 'DELETE', 'INSERT', 'UPDATE', 'ALTER', 'CREATE', 'TRUNCATE']
|
|
323
|
+
for op in destructive:
|
|
324
|
+
# Use word boundary matching to avoid matching "CREATED_AT" with "CREATE"
|
|
325
|
+
if re.search(r'\b' + op + r'\b', query):
|
|
326
|
+
return False
|
|
327
|
+
return True
|
|
328
|
+
|
|
329
|
+
return [
|
|
330
|
+
AgentCapability(
|
|
331
|
+
name="query_database",
|
|
332
|
+
description="Execute read-only SQL queries",
|
|
333
|
+
action_types=[ActionType.DATABASE_QUERY],
|
|
334
|
+
parameter_schema={
|
|
335
|
+
"type": "object",
|
|
336
|
+
"properties": {
|
|
337
|
+
"query": {"type": "string"},
|
|
338
|
+
"database": {"type": "string"}
|
|
339
|
+
},
|
|
340
|
+
"required": ["query"]
|
|
341
|
+
},
|
|
342
|
+
validator=validate_sql_query
|
|
343
|
+
)
|
|
344
|
+
]
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def create_data_analyst_capabilities() -> List[AgentCapability]:
|
|
348
|
+
"""
|
|
349
|
+
Example: Create capabilities for a data analyst agent
|
|
350
|
+
|
|
351
|
+
This agent can read files and query databases, but cannot modify anything.
|
|
352
|
+
"""
|
|
353
|
+
|
|
354
|
+
def validate_safe_file_path(request: ExecutionRequest) -> bool:
|
|
355
|
+
"""Only allow access to /data directory"""
|
|
356
|
+
path = request.parameters.get('path', '')
|
|
357
|
+
return path.startswith('/data/') or path.startswith('./data/')
|
|
358
|
+
|
|
359
|
+
return [
|
|
360
|
+
AgentCapability(
|
|
361
|
+
name="read_data_file",
|
|
362
|
+
description="Read data files from /data directory",
|
|
363
|
+
action_types=[ActionType.FILE_READ],
|
|
364
|
+
parameter_schema={
|
|
365
|
+
"type": "object",
|
|
366
|
+
"properties": {
|
|
367
|
+
"path": {"type": "string", "pattern": "^(/data/|\\./data/).*"}
|
|
368
|
+
},
|
|
369
|
+
"required": ["path"]
|
|
370
|
+
},
|
|
371
|
+
validator=validate_safe_file_path
|
|
372
|
+
),
|
|
373
|
+
AgentCapability(
|
|
374
|
+
name="query_analytics",
|
|
375
|
+
description="Execute analytics queries",
|
|
376
|
+
action_types=[ActionType.DATABASE_QUERY],
|
|
377
|
+
parameter_schema={
|
|
378
|
+
"type": "object",
|
|
379
|
+
"properties": {
|
|
380
|
+
"query": {"type": "string"},
|
|
381
|
+
"database": {"type": "string"}
|
|
382
|
+
},
|
|
383
|
+
"required": ["query"]
|
|
384
|
+
}
|
|
385
|
+
)
|
|
386
|
+
]
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def create_mute_sql_agent(agent_id: str) -> MuteAgentConfig:
|
|
390
|
+
"""
|
|
391
|
+
Create a Mute SQL Agent configuration
|
|
392
|
+
|
|
393
|
+
This agent:
|
|
394
|
+
- Only executes SELECT queries
|
|
395
|
+
- Returns NULL for anything outside this capability
|
|
396
|
+
- Does not try to be conversational or creative
|
|
397
|
+
"""
|
|
398
|
+
return MuteAgentConfig(
|
|
399
|
+
agent_id=agent_id,
|
|
400
|
+
capabilities=create_sql_agent_capabilities(),
|
|
401
|
+
strict_mode=True,
|
|
402
|
+
null_response_message="NULL",
|
|
403
|
+
enable_explanation=False
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def create_mute_data_analyst(agent_id: str, enable_explanation: bool = False) -> MuteAgentConfig:
|
|
408
|
+
"""
|
|
409
|
+
Create a Mute Data Analyst configuration
|
|
410
|
+
|
|
411
|
+
This agent:
|
|
412
|
+
- Can read data files from /data directory
|
|
413
|
+
- Can execute analytics queries
|
|
414
|
+
- Returns NULL for out-of-scope requests
|
|
415
|
+
"""
|
|
416
|
+
return MuteAgentConfig(
|
|
417
|
+
agent_id=agent_id,
|
|
418
|
+
capabilities=create_data_analyst_capabilities(),
|
|
419
|
+
strict_mode=True,
|
|
420
|
+
null_response_message="NULL",
|
|
421
|
+
enable_explanation=enable_explanation
|
|
422
|
+
)
|