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,115 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""
|
|
4
|
+
Declarative policy schema for Agent-OS governance.
|
|
5
|
+
|
|
6
|
+
Defines PolicyDocument and related models that represent policies as
|
|
7
|
+
pure data (JSON/YAML) rather than coupling structure with evaluation logic.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
from enum import Enum
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from pydantic import BaseModel, Field
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class PolicyOperator(str, Enum):
|
|
21
|
+
"""Comparison operators for policy conditions."""
|
|
22
|
+
|
|
23
|
+
EQ = "eq"
|
|
24
|
+
NE = "ne"
|
|
25
|
+
GT = "gt"
|
|
26
|
+
LT = "lt"
|
|
27
|
+
GTE = "gte"
|
|
28
|
+
LTE = "lte"
|
|
29
|
+
IN = "in"
|
|
30
|
+
MATCHES = "matches"
|
|
31
|
+
CONTAINS = "contains"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class PolicyAction(str, Enum):
|
|
35
|
+
"""Actions a policy rule can prescribe."""
|
|
36
|
+
|
|
37
|
+
ALLOW = "allow"
|
|
38
|
+
DENY = "deny"
|
|
39
|
+
AUDIT = "audit"
|
|
40
|
+
BLOCK = "block"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class PolicyCondition(BaseModel):
|
|
44
|
+
"""A single condition evaluated against execution context."""
|
|
45
|
+
|
|
46
|
+
field: str = Field(..., description="Context field, e.g. 'tool_name', 'token_count'")
|
|
47
|
+
operator: PolicyOperator = Field(..., description="Comparison operator")
|
|
48
|
+
value: Any = Field(..., description="Value to compare against")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class PolicyRule(BaseModel):
|
|
52
|
+
"""A single governance rule within a policy document."""
|
|
53
|
+
|
|
54
|
+
name: str
|
|
55
|
+
condition: PolicyCondition
|
|
56
|
+
action: PolicyAction
|
|
57
|
+
priority: int = Field(default=0, description="Higher priority rules are evaluated first")
|
|
58
|
+
message: str = Field(default="", description="Human-readable explanation")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class PolicyDefaults(BaseModel):
|
|
62
|
+
"""Default settings applied when no rule matches."""
|
|
63
|
+
|
|
64
|
+
action: PolicyAction = PolicyAction.ALLOW
|
|
65
|
+
max_tokens: int = 4096
|
|
66
|
+
max_tool_calls: int = 10
|
|
67
|
+
confidence_threshold: float = 0.8
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class PolicyDocument(BaseModel):
|
|
71
|
+
"""Top-level declarative policy document."""
|
|
72
|
+
|
|
73
|
+
version: str = "1.0"
|
|
74
|
+
name: str = "unnamed"
|
|
75
|
+
description: str = ""
|
|
76
|
+
rules: list[PolicyRule] = Field(default_factory=list)
|
|
77
|
+
defaults: PolicyDefaults = Field(default_factory=PolicyDefaults)
|
|
78
|
+
|
|
79
|
+
@classmethod
|
|
80
|
+
def from_yaml(cls, path: str | Path) -> PolicyDocument:
|
|
81
|
+
"""Load a PolicyDocument from a YAML file."""
|
|
82
|
+
try:
|
|
83
|
+
import yaml
|
|
84
|
+
except ImportError as exc:
|
|
85
|
+
raise ImportError("pyyaml is required: pip install pyyaml") from exc
|
|
86
|
+
|
|
87
|
+
path = Path(path)
|
|
88
|
+
with open(path) as f:
|
|
89
|
+
data = yaml.safe_load(f)
|
|
90
|
+
return cls.model_validate(data)
|
|
91
|
+
|
|
92
|
+
def to_yaml(self, path: str | Path) -> None:
|
|
93
|
+
"""Serialize this PolicyDocument to a YAML file."""
|
|
94
|
+
try:
|
|
95
|
+
import yaml
|
|
96
|
+
except ImportError as exc:
|
|
97
|
+
raise ImportError("pyyaml is required: pip install pyyaml") from exc
|
|
98
|
+
|
|
99
|
+
path = Path(path)
|
|
100
|
+
with open(path, "w") as f:
|
|
101
|
+
yaml.dump(self.model_dump(mode="json"), f, default_flow_style=False, sort_keys=False)
|
|
102
|
+
|
|
103
|
+
@classmethod
|
|
104
|
+
def from_json(cls, path: str | Path) -> PolicyDocument:
|
|
105
|
+
"""Load a PolicyDocument from a JSON file."""
|
|
106
|
+
path = Path(path)
|
|
107
|
+
with open(path) as f:
|
|
108
|
+
data = json.load(f)
|
|
109
|
+
return cls.model_validate(data)
|
|
110
|
+
|
|
111
|
+
def to_json(self, path: str | Path) -> None:
|
|
112
|
+
"""Serialize this PolicyDocument to a JSON file."""
|
|
113
|
+
path = Path(path)
|
|
114
|
+
with open(path, "w") as f:
|
|
115
|
+
json.dump(self.model_dump(mode="json"), f, indent=2)
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""
|
|
4
|
+
Cross-project shared policy language for Agent-OS and Agent-Mesh.
|
|
5
|
+
|
|
6
|
+
Provides a unified governance schema (SharedPolicySchema) with rules,
|
|
7
|
+
conditions, and an evaluator that works across both projects. Includes
|
|
8
|
+
YAML serialization and a bridge to the existing PolicyDocument format.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from datetime import datetime, timezone
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from pydantic import BaseModel, Field
|
|
20
|
+
|
|
21
|
+
from .schema import (
|
|
22
|
+
PolicyAction,
|
|
23
|
+
PolicyCondition,
|
|
24
|
+
PolicyDocument,
|
|
25
|
+
PolicyOperator,
|
|
26
|
+
PolicyRule,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
# Enums / literals kept as plain strings for cross-project portability
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
VALID_SCOPES = ("agent", "tool", "flow", "mesh")
|
|
34
|
+
VALID_ACTIONS = ("allow", "deny", "audit", "escalate", "rate_limit")
|
|
35
|
+
VALID_OPERATORS = ("eq", "ne", "in", "not_in", "gt", "lt", "matches")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# ---------------------------------------------------------------------------
|
|
39
|
+
# Core dataclasses
|
|
40
|
+
# ---------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class Condition:
|
|
45
|
+
"""A single predicate evaluated against an execution context."""
|
|
46
|
+
|
|
47
|
+
field: str
|
|
48
|
+
operator: str
|
|
49
|
+
value: Any
|
|
50
|
+
|
|
51
|
+
def __post_init__(self) -> None:
|
|
52
|
+
if self.operator not in VALID_OPERATORS:
|
|
53
|
+
raise ValueError(
|
|
54
|
+
f"Invalid operator '{self.operator}', must be one of {VALID_OPERATORS}"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass
|
|
59
|
+
class SharedPolicyRule:
|
|
60
|
+
"""A single governance rule within a shared policy."""
|
|
61
|
+
|
|
62
|
+
id: str
|
|
63
|
+
action: str
|
|
64
|
+
conditions: list[Condition] = field(default_factory=list)
|
|
65
|
+
priority: int = 0
|
|
66
|
+
|
|
67
|
+
def __post_init__(self) -> None:
|
|
68
|
+
if self.action not in VALID_ACTIONS:
|
|
69
|
+
raise ValueError(
|
|
70
|
+
f"Invalid action '{self.action}', must be one of {VALID_ACTIONS}"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# ---------------------------------------------------------------------------
|
|
75
|
+
# Schema model (Pydantic for validation & serialization)
|
|
76
|
+
# ---------------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class SharedPolicySchema(BaseModel):
|
|
80
|
+
"""Unified policy schema that works across Agent-OS and Agent-Mesh."""
|
|
81
|
+
|
|
82
|
+
version: str = "1.0"
|
|
83
|
+
name: str
|
|
84
|
+
description: str = ""
|
|
85
|
+
scope: str
|
|
86
|
+
rules: list[dict[str, Any]] = Field(default_factory=list)
|
|
87
|
+
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
88
|
+
|
|
89
|
+
def model_post_init(self, __context: Any) -> None:
|
|
90
|
+
if self.scope not in VALID_SCOPES:
|
|
91
|
+
raise ValueError(
|
|
92
|
+
f"Invalid scope '{self.scope}', must be one of {VALID_SCOPES}"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
def parsed_rules(self) -> list[SharedPolicyRule]:
|
|
96
|
+
"""Deserialize raw rule dicts into SharedPolicyRule objects."""
|
|
97
|
+
result: list[SharedPolicyRule] = []
|
|
98
|
+
for raw in self.rules:
|
|
99
|
+
conditions = [
|
|
100
|
+
Condition(field=c["field"], operator=c["operator"], value=c["value"])
|
|
101
|
+
for c in raw.get("conditions", [])
|
|
102
|
+
]
|
|
103
|
+
result.append(
|
|
104
|
+
SharedPolicyRule(
|
|
105
|
+
id=raw["id"],
|
|
106
|
+
action=raw["action"],
|
|
107
|
+
conditions=conditions,
|
|
108
|
+
priority=raw.get("priority", 0),
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
return result
|
|
112
|
+
|
|
113
|
+
# -- YAML I/O ----------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
@classmethod
|
|
116
|
+
def from_yaml(cls, path: str | Path) -> SharedPolicySchema:
|
|
117
|
+
"""Load a SharedPolicySchema from a YAML file."""
|
|
118
|
+
try:
|
|
119
|
+
import yaml
|
|
120
|
+
except ImportError as exc:
|
|
121
|
+
raise ImportError("pyyaml is required: pip install pyyaml") from exc
|
|
122
|
+
|
|
123
|
+
path = Path(path)
|
|
124
|
+
with open(path) as f:
|
|
125
|
+
data = yaml.safe_load(f)
|
|
126
|
+
return cls.model_validate(data)
|
|
127
|
+
|
|
128
|
+
def to_yaml(self, path: str | Path) -> None:
|
|
129
|
+
"""Serialize this SharedPolicySchema to a YAML file."""
|
|
130
|
+
try:
|
|
131
|
+
import yaml
|
|
132
|
+
except ImportError as exc:
|
|
133
|
+
raise ImportError("pyyaml is required: pip install pyyaml") from exc
|
|
134
|
+
|
|
135
|
+
path = Path(path)
|
|
136
|
+
with open(path, "w") as f:
|
|
137
|
+
yaml.dump(
|
|
138
|
+
self.model_dump(mode="json"),
|
|
139
|
+
f,
|
|
140
|
+
default_flow_style=False,
|
|
141
|
+
sort_keys=False,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# ---------------------------------------------------------------------------
|
|
146
|
+
# Decision
|
|
147
|
+
# ---------------------------------------------------------------------------
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@dataclass
|
|
151
|
+
class SharedPolicyDecision:
|
|
152
|
+
"""Result of evaluating shared policy rules against a context."""
|
|
153
|
+
|
|
154
|
+
allowed: bool = True
|
|
155
|
+
action: str = "allow"
|
|
156
|
+
matched_rule_id: str | None = None
|
|
157
|
+
reason: str = "No rules matched; default allow"
|
|
158
|
+
audit: dict[str, Any] = field(default_factory=dict)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
# ---------------------------------------------------------------------------
|
|
162
|
+
# Evaluator
|
|
163
|
+
# ---------------------------------------------------------------------------
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class SharedPolicyEvaluator:
|
|
167
|
+
"""Evaluates SharedPolicyRules against an execution context dict."""
|
|
168
|
+
|
|
169
|
+
def evaluate(
|
|
170
|
+
self,
|
|
171
|
+
context: dict[str, Any],
|
|
172
|
+
rules: list[SharedPolicyRule],
|
|
173
|
+
) -> SharedPolicyDecision:
|
|
174
|
+
"""Evaluate *rules* against *context*, returning a decision.
|
|
175
|
+
|
|
176
|
+
Rules are sorted by priority descending. The first rule whose
|
|
177
|
+
**all** conditions match determines the outcome.
|
|
178
|
+
"""
|
|
179
|
+
sorted_rules = sorted(rules, key=lambda r: r.priority, reverse=True)
|
|
180
|
+
|
|
181
|
+
for rule in sorted_rules:
|
|
182
|
+
if all(_eval_condition(c, context) for c in rule.conditions):
|
|
183
|
+
allowed = rule.action in ("allow", "audit")
|
|
184
|
+
return SharedPolicyDecision(
|
|
185
|
+
allowed=allowed,
|
|
186
|
+
action=rule.action,
|
|
187
|
+
matched_rule_id=rule.id,
|
|
188
|
+
reason=f"Matched rule '{rule.id}'",
|
|
189
|
+
audit={
|
|
190
|
+
"rule_id": rule.id,
|
|
191
|
+
"action": rule.action,
|
|
192
|
+
"context_snapshot": context,
|
|
193
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
194
|
+
},
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
return SharedPolicyDecision()
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _eval_condition(condition: Condition, context: dict[str, Any]) -> bool:
|
|
201
|
+
"""Evaluate a single Condition against the context."""
|
|
202
|
+
ctx_value = context.get(condition.field)
|
|
203
|
+
if ctx_value is None:
|
|
204
|
+
return False
|
|
205
|
+
|
|
206
|
+
op = condition.operator
|
|
207
|
+
target = condition.value
|
|
208
|
+
|
|
209
|
+
if op == "eq":
|
|
210
|
+
return ctx_value == target # type: ignore[no-any-return]
|
|
211
|
+
if op == "ne":
|
|
212
|
+
return ctx_value != target # type: ignore[no-any-return]
|
|
213
|
+
if op == "gt":
|
|
214
|
+
return ctx_value > target # type: ignore[no-any-return]
|
|
215
|
+
if op == "lt":
|
|
216
|
+
return ctx_value < target # type: ignore[no-any-return]
|
|
217
|
+
if op == "in":
|
|
218
|
+
return ctx_value in target # type: ignore[no-any-return]
|
|
219
|
+
if op == "not_in":
|
|
220
|
+
return ctx_value not in target # type: ignore[no-any-return]
|
|
221
|
+
if op == "matches":
|
|
222
|
+
return bool(re.search(str(target), str(ctx_value)))
|
|
223
|
+
|
|
224
|
+
return False
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
# ---------------------------------------------------------------------------
|
|
228
|
+
# Bridge helpers — SharedPolicySchema <-> PolicyDocument
|
|
229
|
+
# ---------------------------------------------------------------------------
|
|
230
|
+
|
|
231
|
+
_ACTION_MAP_TO_POLICY: dict[str, PolicyAction] = {
|
|
232
|
+
"allow": PolicyAction.ALLOW,
|
|
233
|
+
"deny": PolicyAction.DENY,
|
|
234
|
+
"audit": PolicyAction.AUDIT,
|
|
235
|
+
"escalate": PolicyAction.BLOCK,
|
|
236
|
+
"rate_limit": PolicyAction.BLOCK,
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
_OPERATOR_MAP_TO_POLICY: dict[str, PolicyOperator] = {
|
|
240
|
+
"eq": PolicyOperator.EQ,
|
|
241
|
+
"ne": PolicyOperator.NE,
|
|
242
|
+
"gt": PolicyOperator.GT,
|
|
243
|
+
"lt": PolicyOperator.LT,
|
|
244
|
+
"in": PolicyOperator.IN,
|
|
245
|
+
"not_in": PolicyOperator.NE,
|
|
246
|
+
"matches": PolicyOperator.MATCHES,
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
_ACTION_MAP_FROM_POLICY: dict[PolicyAction, str] = {
|
|
250
|
+
PolicyAction.ALLOW: "allow",
|
|
251
|
+
PolicyAction.DENY: "deny",
|
|
252
|
+
PolicyAction.AUDIT: "audit",
|
|
253
|
+
PolicyAction.BLOCK: "deny",
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
_OPERATOR_MAP_FROM_POLICY: dict[PolicyOperator, str] = {
|
|
257
|
+
PolicyOperator.EQ: "eq",
|
|
258
|
+
PolicyOperator.NE: "ne",
|
|
259
|
+
PolicyOperator.GT: "gt",
|
|
260
|
+
PolicyOperator.LT: "lt",
|
|
261
|
+
PolicyOperator.GTE: "gt",
|
|
262
|
+
PolicyOperator.LTE: "lt",
|
|
263
|
+
PolicyOperator.IN: "in",
|
|
264
|
+
PolicyOperator.CONTAINS: "matches",
|
|
265
|
+
PolicyOperator.MATCHES: "matches",
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def shared_to_policy_document(schema: SharedPolicySchema) -> PolicyDocument:
|
|
270
|
+
"""Convert a SharedPolicySchema into the existing PolicyDocument format."""
|
|
271
|
+
doc_rules: list[PolicyRule] = []
|
|
272
|
+
for rule in schema.parsed_rules():
|
|
273
|
+
if not rule.conditions:
|
|
274
|
+
continue
|
|
275
|
+
first_cond = rule.conditions[0]
|
|
276
|
+
doc_rules.append(
|
|
277
|
+
PolicyRule(
|
|
278
|
+
name=rule.id,
|
|
279
|
+
condition=PolicyCondition(
|
|
280
|
+
field=first_cond.field,
|
|
281
|
+
operator=_OPERATOR_MAP_TO_POLICY.get(
|
|
282
|
+
first_cond.operator, PolicyOperator.EQ
|
|
283
|
+
),
|
|
284
|
+
value=first_cond.value,
|
|
285
|
+
),
|
|
286
|
+
action=_ACTION_MAP_TO_POLICY.get(rule.action, PolicyAction.DENY),
|
|
287
|
+
priority=rule.priority,
|
|
288
|
+
message=f"Converted from shared rule '{rule.id}'",
|
|
289
|
+
)
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
return PolicyDocument(
|
|
293
|
+
version=schema.version,
|
|
294
|
+
name=schema.name,
|
|
295
|
+
description=schema.description,
|
|
296
|
+
rules=doc_rules,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def policy_document_to_shared(
|
|
301
|
+
doc: PolicyDocument,
|
|
302
|
+
scope: str = "agent",
|
|
303
|
+
) -> SharedPolicySchema:
|
|
304
|
+
"""Convert an existing PolicyDocument into a SharedPolicySchema."""
|
|
305
|
+
raw_rules: list[dict[str, Any]] = []
|
|
306
|
+
for rule in doc.rules:
|
|
307
|
+
cond = rule.condition
|
|
308
|
+
raw_rules.append(
|
|
309
|
+
{
|
|
310
|
+
"id": rule.name,
|
|
311
|
+
"action": _ACTION_MAP_FROM_POLICY.get(rule.action, "deny"),
|
|
312
|
+
"priority": rule.priority,
|
|
313
|
+
"conditions": [
|
|
314
|
+
{
|
|
315
|
+
"field": cond.field,
|
|
316
|
+
"operator": _OPERATOR_MAP_FROM_POLICY.get(
|
|
317
|
+
cond.operator, "eq"
|
|
318
|
+
),
|
|
319
|
+
"value": cond.value,
|
|
320
|
+
}
|
|
321
|
+
],
|
|
322
|
+
}
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
return SharedPolicySchema(
|
|
326
|
+
version=doc.version,
|
|
327
|
+
name=doc.name,
|
|
328
|
+
description=doc.description,
|
|
329
|
+
scope=scope,
|
|
330
|
+
rules=raw_rules,
|
|
331
|
+
)
|