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,343 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""LlamaFirewall integration adapter — chain Meta's LlamaFirewall with Agent OS for defense-in-depth."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from enum import Enum
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from agent_os.prompt_injection import (
|
|
13
|
+
DetectionConfig,
|
|
14
|
+
DetectionResult,
|
|
15
|
+
PromptInjectionDetector,
|
|
16
|
+
ThreatLevel,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
# Enums
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
class FirewallMode(Enum):
|
|
27
|
+
"""Operating mode for the combined firewall."""
|
|
28
|
+
LLAMAFIREWALL_ONLY = "llamafirewall_only"
|
|
29
|
+
AGENT_OS_ONLY = "agent_os_only"
|
|
30
|
+
CHAIN_BOTH = "chain_both"
|
|
31
|
+
VOTE_MAJORITY = "vote_majority"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class FirewallVerdict(Enum):
|
|
35
|
+
"""Unified verdict across scanners."""
|
|
36
|
+
SAFE = "safe"
|
|
37
|
+
SUSPICIOUS = "suspicious"
|
|
38
|
+
BLOCKED = "blocked"
|
|
39
|
+
ERROR = "error"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# ---------------------------------------------------------------------------
|
|
43
|
+
# Result dataclass
|
|
44
|
+
# ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class FirewallResult:
|
|
48
|
+
"""Combined result from LlamaFirewall and/or Agent OS scanning."""
|
|
49
|
+
verdict: FirewallVerdict
|
|
50
|
+
source: str # "llamafirewall", "agent_os", "combined"
|
|
51
|
+
score: float # 0.0 (safe) to 1.0 (blocked)
|
|
52
|
+
details: dict[str, Any] = field(default_factory=dict)
|
|
53
|
+
prompt_guard_result: dict[str, Any] | None = None
|
|
54
|
+
alignment_check_result: dict[str, Any] | None = None
|
|
55
|
+
code_shield_result: dict[str, Any] | None = None
|
|
56
|
+
agent_os_result: DetectionResult | None = None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# ---------------------------------------------------------------------------
|
|
60
|
+
# LlamaFirewallAdapter
|
|
61
|
+
# ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
class LlamaFirewallAdapter:
|
|
64
|
+
"""Adapter that chains Meta's LlamaFirewall with Agent OS prompt injection detection.
|
|
65
|
+
|
|
66
|
+
Usage::
|
|
67
|
+
|
|
68
|
+
adapter = LlamaFirewallAdapter(mode=FirewallMode.CHAIN_BOTH)
|
|
69
|
+
result = await adapter.scan_prompt("ignore previous instructions")
|
|
70
|
+
if result.verdict == FirewallVerdict.BLOCKED:
|
|
71
|
+
print("Blocked!")
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def __init__(
|
|
75
|
+
self,
|
|
76
|
+
mode: FirewallMode = FirewallMode.CHAIN_BOTH,
|
|
77
|
+
agent_os_config: DetectionConfig | None = None,
|
|
78
|
+
) -> None:
|
|
79
|
+
self._mode = mode
|
|
80
|
+
self._detector = PromptInjectionDetector(config=agent_os_config)
|
|
81
|
+
self._llama_available = False
|
|
82
|
+
|
|
83
|
+
# Lazy import of llamafirewall
|
|
84
|
+
try:
|
|
85
|
+
import llamafirewall # noqa: F401
|
|
86
|
+
self._llama_available = True
|
|
87
|
+
except ImportError:
|
|
88
|
+
self._llama_available = False
|
|
89
|
+
|
|
90
|
+
# Fallback if llama is required but not installed
|
|
91
|
+
if mode in (FirewallMode.LLAMAFIREWALL_ONLY, FirewallMode.CHAIN_BOTH, FirewallMode.VOTE_MAJORITY):
|
|
92
|
+
if not self._llama_available:
|
|
93
|
+
logger.warning(
|
|
94
|
+
"llamafirewall package not installed — falling back to AGENT_OS_ONLY mode"
|
|
95
|
+
)
|
|
96
|
+
if mode == FirewallMode.LLAMAFIREWALL_ONLY:
|
|
97
|
+
self._mode = FirewallMode.AGENT_OS_ONLY
|
|
98
|
+
|
|
99
|
+
# -- public API ---------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
async def scan_prompt(
|
|
102
|
+
self,
|
|
103
|
+
prompt: str,
|
|
104
|
+
context: str | None = None,
|
|
105
|
+
) -> FirewallResult:
|
|
106
|
+
"""Scan a prompt using the configured mode (async)."""
|
|
107
|
+
mode = self._mode
|
|
108
|
+
|
|
109
|
+
if mode == FirewallMode.AGENT_OS_ONLY:
|
|
110
|
+
aos_result = self._run_agent_os(prompt)
|
|
111
|
+
return self._combine_results(None, aos_result, mode)
|
|
112
|
+
|
|
113
|
+
if mode == FirewallMode.LLAMAFIREWALL_ONLY:
|
|
114
|
+
llama_result = self._run_llamafirewall(prompt, context)
|
|
115
|
+
return self._combine_results(llama_result, None, mode)
|
|
116
|
+
|
|
117
|
+
# CHAIN_BOTH or VOTE_MAJORITY — run both
|
|
118
|
+
llama_result = self._run_llamafirewall(prompt, context) if self._llama_available else None
|
|
119
|
+
aos_result = self._run_agent_os(prompt)
|
|
120
|
+
return self._combine_results(llama_result, aos_result, mode)
|
|
121
|
+
|
|
122
|
+
def scan_prompt_sync(
|
|
123
|
+
self,
|
|
124
|
+
prompt: str,
|
|
125
|
+
context: str | None = None,
|
|
126
|
+
) -> FirewallResult:
|
|
127
|
+
"""Scan a prompt using the configured mode (synchronous)."""
|
|
128
|
+
mode = self._mode
|
|
129
|
+
|
|
130
|
+
if mode == FirewallMode.AGENT_OS_ONLY:
|
|
131
|
+
aos_result = self._run_agent_os(prompt)
|
|
132
|
+
return self._combine_results(None, aos_result, mode)
|
|
133
|
+
|
|
134
|
+
if mode == FirewallMode.LLAMAFIREWALL_ONLY:
|
|
135
|
+
llama_result = self._run_llamafirewall(prompt, context)
|
|
136
|
+
return self._combine_results(llama_result, None, mode)
|
|
137
|
+
|
|
138
|
+
# CHAIN_BOTH or VOTE_MAJORITY
|
|
139
|
+
llama_result = self._run_llamafirewall(prompt, context) if self._llama_available else None
|
|
140
|
+
aos_result = self._run_agent_os(prompt)
|
|
141
|
+
return self._combine_results(llama_result, aos_result, mode)
|
|
142
|
+
|
|
143
|
+
async def scan_code(
|
|
144
|
+
self,
|
|
145
|
+
code: str,
|
|
146
|
+
language: str = "python",
|
|
147
|
+
) -> FirewallResult:
|
|
148
|
+
"""Scan code using CodeShield if available, otherwise return SAFE with warning."""
|
|
149
|
+
if not self._llama_available:
|
|
150
|
+
return FirewallResult(
|
|
151
|
+
verdict=FirewallVerdict.SAFE,
|
|
152
|
+
source="agent_os",
|
|
153
|
+
score=0.0,
|
|
154
|
+
details={"warning": "CodeShield not available — llamafirewall not installed"},
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
from llamafirewall import CodeShield # type: ignore[import-untyped]
|
|
159
|
+
shield = CodeShield()
|
|
160
|
+
result = shield.scan(code, language=language)
|
|
161
|
+
verdict = self._map_llama_verdict(result.get("verdict", "safe"))
|
|
162
|
+
return FirewallResult(
|
|
163
|
+
verdict=verdict,
|
|
164
|
+
source="llamafirewall",
|
|
165
|
+
score=result.get("score", 0.0),
|
|
166
|
+
details={"language": language},
|
|
167
|
+
code_shield_result=result,
|
|
168
|
+
)
|
|
169
|
+
except ImportError:
|
|
170
|
+
return FirewallResult(
|
|
171
|
+
verdict=FirewallVerdict.SAFE,
|
|
172
|
+
source="agent_os",
|
|
173
|
+
score=0.0,
|
|
174
|
+
details={"warning": "CodeShield import failed"},
|
|
175
|
+
)
|
|
176
|
+
except Exception as exc:
|
|
177
|
+
logger.error("CodeShield scan error: %s", exc, exc_info=True)
|
|
178
|
+
return FirewallResult(
|
|
179
|
+
verdict=FirewallVerdict.ERROR,
|
|
180
|
+
source="llamafirewall",
|
|
181
|
+
score=0.0,
|
|
182
|
+
details={"error": str(exc)},
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# -- internal scanners --------------------------------------------------
|
|
186
|
+
|
|
187
|
+
def _run_llamafirewall(
|
|
188
|
+
self,
|
|
189
|
+
prompt: str,
|
|
190
|
+
context: str | None = None,
|
|
191
|
+
) -> dict[str, Any]:
|
|
192
|
+
"""Call LlamaFirewall's scan API with graceful error handling."""
|
|
193
|
+
try:
|
|
194
|
+
from llamafirewall import LlamaFirewall as LF # type: ignore[import-untyped]
|
|
195
|
+
fw = LF()
|
|
196
|
+
result = fw.scan(prompt, context=context)
|
|
197
|
+
return {
|
|
198
|
+
"verdict": result.get("verdict", "safe"),
|
|
199
|
+
"score": result.get("score", 0.0),
|
|
200
|
+
"prompt_guard": result.get("prompt_guard"),
|
|
201
|
+
"alignment_check": result.get("alignment_check"),
|
|
202
|
+
}
|
|
203
|
+
except ImportError:
|
|
204
|
+
logger.warning("llamafirewall not importable — returning empty result")
|
|
205
|
+
return {"verdict": "error", "score": 0.0, "error": "import_failed"}
|
|
206
|
+
except Exception as exc:
|
|
207
|
+
logger.error("LlamaFirewall scan error: %s", exc, exc_info=True)
|
|
208
|
+
return {"verdict": "error", "score": 0.0, "error": str(exc)}
|
|
209
|
+
|
|
210
|
+
def _run_agent_os(self, prompt: str) -> DetectionResult:
|
|
211
|
+
"""Run Agent OS PromptInjectionDetector — always available."""
|
|
212
|
+
return self._detector.detect(prompt, source="llamafirewall_adapter")
|
|
213
|
+
|
|
214
|
+
# -- result combination -------------------------------------------------
|
|
215
|
+
|
|
216
|
+
def _combine_results(
|
|
217
|
+
self,
|
|
218
|
+
llama_result: dict[str, Any] | None,
|
|
219
|
+
aos_result: DetectionResult | None,
|
|
220
|
+
mode: FirewallMode,
|
|
221
|
+
) -> FirewallResult:
|
|
222
|
+
"""Merge results from both scanners based on the operating mode."""
|
|
223
|
+
llama_score = llama_result.get("score", 0.0) if llama_result else 0.0
|
|
224
|
+
llama_verdict_str = llama_result.get("verdict", "safe") if llama_result else "safe"
|
|
225
|
+
llama_verdict = self._map_llama_verdict(llama_verdict_str)
|
|
226
|
+
|
|
227
|
+
aos_score = aos_result.confidence if aos_result else 0.0
|
|
228
|
+
aos_verdict = self._agent_os_verdict(aos_result) if aos_result else FirewallVerdict.SAFE
|
|
229
|
+
|
|
230
|
+
# Single-scanner modes
|
|
231
|
+
if mode == FirewallMode.AGENT_OS_ONLY:
|
|
232
|
+
return FirewallResult(
|
|
233
|
+
verdict=aos_verdict,
|
|
234
|
+
source="agent_os",
|
|
235
|
+
score=aos_score,
|
|
236
|
+
details={"mode": mode.value},
|
|
237
|
+
agent_os_result=aos_result,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
if mode == FirewallMode.LLAMAFIREWALL_ONLY:
|
|
241
|
+
return FirewallResult(
|
|
242
|
+
verdict=llama_verdict,
|
|
243
|
+
source="llamafirewall",
|
|
244
|
+
score=llama_score,
|
|
245
|
+
details={"mode": mode.value},
|
|
246
|
+
prompt_guard_result=llama_result.get("prompt_guard") if llama_result else None,
|
|
247
|
+
alignment_check_result=llama_result.get("alignment_check") if llama_result else None,
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
# CHAIN_BOTH: block if either blocks, use max score (most conservative)
|
|
251
|
+
if mode == FirewallMode.CHAIN_BOTH:
|
|
252
|
+
combined_score = max(llama_score, aos_score)
|
|
253
|
+
if llama_verdict == FirewallVerdict.BLOCKED or aos_verdict == FirewallVerdict.BLOCKED:
|
|
254
|
+
combined_verdict = FirewallVerdict.BLOCKED
|
|
255
|
+
elif llama_verdict == FirewallVerdict.SUSPICIOUS or aos_verdict == FirewallVerdict.SUSPICIOUS:
|
|
256
|
+
combined_verdict = FirewallVerdict.SUSPICIOUS
|
|
257
|
+
elif llama_verdict == FirewallVerdict.ERROR or aos_verdict == FirewallVerdict.ERROR:
|
|
258
|
+
combined_verdict = FirewallVerdict.ERROR
|
|
259
|
+
else:
|
|
260
|
+
combined_verdict = FirewallVerdict.SAFE
|
|
261
|
+
|
|
262
|
+
return FirewallResult(
|
|
263
|
+
verdict=combined_verdict,
|
|
264
|
+
source="combined",
|
|
265
|
+
score=combined_score,
|
|
266
|
+
details={"mode": mode.value, "llama_verdict": llama_verdict_str, "aos_verdict": aos_verdict.value},
|
|
267
|
+
prompt_guard_result=llama_result.get("prompt_guard") if llama_result else None,
|
|
268
|
+
alignment_check_result=llama_result.get("alignment_check") if llama_result else None,
|
|
269
|
+
agent_os_result=aos_result,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
# VOTE_MAJORITY: require both to agree for block, use average score
|
|
273
|
+
if mode == FirewallMode.VOTE_MAJORITY:
|
|
274
|
+
combined_score = (llama_score + aos_score) / 2.0
|
|
275
|
+
block_votes = sum([
|
|
276
|
+
llama_verdict == FirewallVerdict.BLOCKED,
|
|
277
|
+
aos_verdict == FirewallVerdict.BLOCKED,
|
|
278
|
+
])
|
|
279
|
+
if block_votes >= 2:
|
|
280
|
+
combined_verdict = FirewallVerdict.BLOCKED
|
|
281
|
+
elif block_votes == 1:
|
|
282
|
+
combined_verdict = FirewallVerdict.SUSPICIOUS
|
|
283
|
+
else:
|
|
284
|
+
combined_verdict = FirewallVerdict.SAFE
|
|
285
|
+
|
|
286
|
+
return FirewallResult(
|
|
287
|
+
verdict=combined_verdict,
|
|
288
|
+
source="combined",
|
|
289
|
+
score=combined_score,
|
|
290
|
+
details={
|
|
291
|
+
"mode": mode.value,
|
|
292
|
+
"block_votes": block_votes,
|
|
293
|
+
"llama_verdict": llama_verdict_str,
|
|
294
|
+
"aos_verdict": aos_verdict.value,
|
|
295
|
+
},
|
|
296
|
+
prompt_guard_result=llama_result.get("prompt_guard") if llama_result else None,
|
|
297
|
+
alignment_check_result=llama_result.get("alignment_check") if llama_result else None,
|
|
298
|
+
agent_os_result=aos_result,
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
# Fallback (should not reach here)
|
|
302
|
+
return FirewallResult(
|
|
303
|
+
verdict=FirewallVerdict.ERROR,
|
|
304
|
+
source="combined",
|
|
305
|
+
score=0.0,
|
|
306
|
+
details={"error": f"unknown mode: {mode}"},
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
# -- helpers ------------------------------------------------------------
|
|
310
|
+
|
|
311
|
+
@staticmethod
|
|
312
|
+
def _map_llama_verdict(verdict_str: str) -> FirewallVerdict:
|
|
313
|
+
"""Map LlamaFirewall string verdict to FirewallVerdict."""
|
|
314
|
+
mapping = {
|
|
315
|
+
"safe": FirewallVerdict.SAFE,
|
|
316
|
+
"suspicious": FirewallVerdict.SUSPICIOUS,
|
|
317
|
+
"blocked": FirewallVerdict.BLOCKED,
|
|
318
|
+
"error": FirewallVerdict.ERROR,
|
|
319
|
+
"malicious": FirewallVerdict.BLOCKED,
|
|
320
|
+
"benign": FirewallVerdict.SAFE,
|
|
321
|
+
}
|
|
322
|
+
return mapping.get(verdict_str.lower(), FirewallVerdict.SUSPICIOUS)
|
|
323
|
+
|
|
324
|
+
@staticmethod
|
|
325
|
+
def _agent_os_verdict(result: DetectionResult) -> FirewallVerdict:
|
|
326
|
+
"""Map Agent OS DetectionResult to FirewallVerdict."""
|
|
327
|
+
if not result.is_injection:
|
|
328
|
+
return FirewallVerdict.SAFE
|
|
329
|
+
if result.threat_level in (ThreatLevel.HIGH, ThreatLevel.CRITICAL):
|
|
330
|
+
return FirewallVerdict.BLOCKED
|
|
331
|
+
return FirewallVerdict.SUSPICIOUS
|
|
332
|
+
|
|
333
|
+
@property
|
|
334
|
+
def available_scanners(self) -> list[str]:
|
|
335
|
+
"""Return list of active scanner names."""
|
|
336
|
+
scanners: list[str] = []
|
|
337
|
+
if self._mode in (FirewallMode.AGENT_OS_ONLY, FirewallMode.CHAIN_BOTH, FirewallMode.VOTE_MAJORITY):
|
|
338
|
+
scanners.append("agent_os")
|
|
339
|
+
if self._llama_available and self._mode in (
|
|
340
|
+
FirewallMode.LLAMAFIREWALL_ONLY, FirewallMode.CHAIN_BOTH, FirewallMode.VOTE_MAJORITY,
|
|
341
|
+
):
|
|
342
|
+
scanners.append("llamafirewall")
|
|
343
|
+
return scanners
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""
|
|
4
|
+
LlamaIndex Integration
|
|
5
|
+
|
|
6
|
+
Wraps LlamaIndex query engines, retrievers, and agents with Agent OS governance.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
from agent_os.integrations import LlamaIndexKernel
|
|
10
|
+
|
|
11
|
+
kernel = LlamaIndexKernel()
|
|
12
|
+
governed_engine = kernel.wrap(my_query_engine)
|
|
13
|
+
|
|
14
|
+
# Now all queries go through Agent OS governance
|
|
15
|
+
result = governed_engine.query("What is the meaning of life?")
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from typing import Any, Optional
|
|
19
|
+
|
|
20
|
+
from .base import BaseIntegration, GovernancePolicy
|
|
21
|
+
from .langchain_adapter import PolicyViolationError
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class LlamaIndexKernel(BaseIntegration):
|
|
25
|
+
"""
|
|
26
|
+
LlamaIndex adapter for Agent OS.
|
|
27
|
+
|
|
28
|
+
Supports:
|
|
29
|
+
- QueryEngine (query, aquery)
|
|
30
|
+
- RetrieverQueryEngine
|
|
31
|
+
- ChatEngine (chat, achat, stream_chat)
|
|
32
|
+
- AgentRunner (chat, query)
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(self, policy: Optional[GovernancePolicy] = None):
|
|
36
|
+
super().__init__(policy)
|
|
37
|
+
self._wrapped_agents: dict[int, Any] = {}
|
|
38
|
+
self._stopped: dict[str, bool] = {}
|
|
39
|
+
|
|
40
|
+
def wrap(self, agent: Any) -> Any:
|
|
41
|
+
"""
|
|
42
|
+
Wrap a LlamaIndex query engine, chat engine, or agent with governance.
|
|
43
|
+
|
|
44
|
+
Intercepts:
|
|
45
|
+
- query() / aquery()
|
|
46
|
+
- chat() / achat()
|
|
47
|
+
- stream_chat()
|
|
48
|
+
- retrieve()
|
|
49
|
+
"""
|
|
50
|
+
agent_id = getattr(agent, 'name', None) or f"llamaindex-{id(agent)}"
|
|
51
|
+
ctx = self.create_context(agent_id)
|
|
52
|
+
|
|
53
|
+
self._wrapped_agents[id(agent)] = agent
|
|
54
|
+
self._stopped[agent_id] = False
|
|
55
|
+
|
|
56
|
+
original = agent
|
|
57
|
+
kernel = self
|
|
58
|
+
|
|
59
|
+
class GovernedLlamaIndexAgent:
|
|
60
|
+
"""LlamaIndex engine wrapped with Agent OS governance"""
|
|
61
|
+
|
|
62
|
+
def __init__(self):
|
|
63
|
+
self._original = original
|
|
64
|
+
self._ctx = ctx
|
|
65
|
+
self._kernel = kernel
|
|
66
|
+
self._agent_id = agent_id
|
|
67
|
+
|
|
68
|
+
def _check_stopped(self):
|
|
69
|
+
if kernel._stopped.get(self._agent_id):
|
|
70
|
+
raise PolicyViolationError(
|
|
71
|
+
f"Agent '{self._agent_id}' is stopped (SIGSTOP)"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def query(self, query_str: Any, **kwargs) -> Any:
|
|
75
|
+
"""Governed query"""
|
|
76
|
+
self._check_stopped()
|
|
77
|
+
|
|
78
|
+
allowed, reason = self._kernel.pre_execute(self._ctx, query_str)
|
|
79
|
+
if not allowed:
|
|
80
|
+
raise PolicyViolationError(reason)
|
|
81
|
+
|
|
82
|
+
result = self._original.query(query_str, **kwargs)
|
|
83
|
+
|
|
84
|
+
valid, reason = self._kernel.post_execute(self._ctx, result)
|
|
85
|
+
if not valid:
|
|
86
|
+
raise PolicyViolationError(reason)
|
|
87
|
+
|
|
88
|
+
return result
|
|
89
|
+
|
|
90
|
+
async def aquery(self, query_str: Any, **kwargs) -> Any:
|
|
91
|
+
"""Governed async query"""
|
|
92
|
+
self._check_stopped()
|
|
93
|
+
|
|
94
|
+
allowed, reason = self._kernel.pre_execute(self._ctx, query_str)
|
|
95
|
+
if not allowed:
|
|
96
|
+
raise PolicyViolationError(reason)
|
|
97
|
+
|
|
98
|
+
result = await self._original.aquery(query_str, **kwargs)
|
|
99
|
+
|
|
100
|
+
valid, reason = self._kernel.post_execute(self._ctx, result)
|
|
101
|
+
if not valid:
|
|
102
|
+
raise PolicyViolationError(reason)
|
|
103
|
+
|
|
104
|
+
return result
|
|
105
|
+
|
|
106
|
+
def chat(self, message: str, **kwargs) -> Any:
|
|
107
|
+
"""Governed chat"""
|
|
108
|
+
self._check_stopped()
|
|
109
|
+
|
|
110
|
+
allowed, reason = self._kernel.pre_execute(self._ctx, message)
|
|
111
|
+
if not allowed:
|
|
112
|
+
raise PolicyViolationError(reason)
|
|
113
|
+
|
|
114
|
+
result = self._original.chat(message, **kwargs)
|
|
115
|
+
|
|
116
|
+
valid, reason = self._kernel.post_execute(self._ctx, result)
|
|
117
|
+
if not valid:
|
|
118
|
+
raise PolicyViolationError(reason)
|
|
119
|
+
|
|
120
|
+
return result
|
|
121
|
+
|
|
122
|
+
async def achat(self, message: str, **kwargs) -> Any:
|
|
123
|
+
"""Governed async chat"""
|
|
124
|
+
self._check_stopped()
|
|
125
|
+
|
|
126
|
+
allowed, reason = self._kernel.pre_execute(self._ctx, message)
|
|
127
|
+
if not allowed:
|
|
128
|
+
raise PolicyViolationError(reason)
|
|
129
|
+
|
|
130
|
+
result = await self._original.achat(message, **kwargs)
|
|
131
|
+
|
|
132
|
+
valid, reason = self._kernel.post_execute(self._ctx, result)
|
|
133
|
+
if not valid:
|
|
134
|
+
raise PolicyViolationError(reason)
|
|
135
|
+
|
|
136
|
+
return result
|
|
137
|
+
|
|
138
|
+
def stream_chat(self, message: str, **kwargs):
|
|
139
|
+
"""Governed streaming chat"""
|
|
140
|
+
self._check_stopped()
|
|
141
|
+
|
|
142
|
+
allowed, reason = self._kernel.pre_execute(self._ctx, message)
|
|
143
|
+
if not allowed:
|
|
144
|
+
raise PolicyViolationError(reason)
|
|
145
|
+
|
|
146
|
+
response = self._original.stream_chat(message, **kwargs)
|
|
147
|
+
|
|
148
|
+
self._kernel.post_execute(self._ctx, None)
|
|
149
|
+
return response
|
|
150
|
+
|
|
151
|
+
def retrieve(self, query_str: Any, **kwargs) -> Any:
|
|
152
|
+
"""Governed retrieve"""
|
|
153
|
+
self._check_stopped()
|
|
154
|
+
|
|
155
|
+
allowed, reason = self._kernel.pre_execute(self._ctx, query_str)
|
|
156
|
+
if not allowed:
|
|
157
|
+
raise PolicyViolationError(reason)
|
|
158
|
+
|
|
159
|
+
result = self._original.retrieve(query_str, **kwargs)
|
|
160
|
+
|
|
161
|
+
self._kernel.post_execute(self._ctx, result)
|
|
162
|
+
return result
|
|
163
|
+
|
|
164
|
+
def __getattr__(self, name):
|
|
165
|
+
return getattr(self._original, name)
|
|
166
|
+
|
|
167
|
+
return GovernedLlamaIndexAgent()
|
|
168
|
+
|
|
169
|
+
def unwrap(self, governed_agent: Any) -> Any:
|
|
170
|
+
"""Get original engine from wrapped version"""
|
|
171
|
+
return governed_agent._original
|
|
172
|
+
|
|
173
|
+
def signal(self, agent_id: str, signal: str):
|
|
174
|
+
"""Send signal to a governed agent"""
|
|
175
|
+
if signal == "SIGSTOP":
|
|
176
|
+
self._stopped[agent_id] = True
|
|
177
|
+
elif signal == "SIGCONT":
|
|
178
|
+
self._stopped[agent_id] = False
|
|
179
|
+
elif signal == "SIGKILL":
|
|
180
|
+
self._stopped[agent_id] = True
|
|
181
|
+
|
|
182
|
+
super().signal(agent_id, signal)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# Convenience function
|
|
186
|
+
def wrap(agent: Any, policy: Optional[GovernancePolicy] = None) -> Any:
|
|
187
|
+
"""Quick wrapper for LlamaIndex engines"""
|
|
188
|
+
return LlamaIndexKernel(policy).wrap(agent)
|