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
agent_os/server/app.py
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""FastAPI application for Agent OS governance API."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
import time
|
|
10
|
+
from datetime import datetime, timezone
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from fastapi import FastAPI, HTTPException, Query, Request
|
|
14
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
15
|
+
from fastapi.responses import JSONResponse
|
|
16
|
+
|
|
17
|
+
from agent_os.server.models import (
|
|
18
|
+
DetectBatchRequest,
|
|
19
|
+
DetectInjectionRequest,
|
|
20
|
+
DetectionBatchResponse,
|
|
21
|
+
DetectionResponse,
|
|
22
|
+
ErrorResponse,
|
|
23
|
+
ExecuteRequest,
|
|
24
|
+
ExecuteResponse,
|
|
25
|
+
HealthResponse,
|
|
26
|
+
MetricsResponse,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _detection_result_to_response(result: Any) -> DetectionResponse:
|
|
33
|
+
"""Convert a ``DetectionResult`` dataclass to a Pydantic response."""
|
|
34
|
+
return DetectionResponse(
|
|
35
|
+
is_injection=result.is_injection,
|
|
36
|
+
threat_level=result.threat_level.value,
|
|
37
|
+
injection_type=result.injection_type.value if result.injection_type else None,
|
|
38
|
+
confidence=result.confidence,
|
|
39
|
+
matched_patterns=list(result.matched_patterns),
|
|
40
|
+
explanation=result.explanation,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class GovServer:
|
|
45
|
+
"""High-level wrapper that owns the FastAPI app and its dependencies."""
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
*,
|
|
50
|
+
title: str = "Agent OS Governance API",
|
|
51
|
+
version: str | None = None,
|
|
52
|
+
) -> None:
|
|
53
|
+
from agent_os import __version__
|
|
54
|
+
from agent_os.health import HealthChecker
|
|
55
|
+
from agent_os.metrics import GovernanceMetrics
|
|
56
|
+
from agent_os.prompt_injection import DetectionConfig, PromptInjectionDetector
|
|
57
|
+
|
|
58
|
+
self._version = version or __version__
|
|
59
|
+
self._detector = PromptInjectionDetector(DetectionConfig(sensitivity="balanced"))
|
|
60
|
+
self._metrics = GovernanceMetrics()
|
|
61
|
+
self._health_checker = HealthChecker(version=self._version)
|
|
62
|
+
self._app = create_app(self, title=title)
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def app(self) -> FastAPI:
|
|
66
|
+
return self._app
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def detector(self) -> Any:
|
|
70
|
+
return self._detector
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def metrics(self) -> Any:
|
|
74
|
+
return self._metrics
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def health_checker(self) -> Any:
|
|
78
|
+
return self._health_checker
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def create_app(
|
|
82
|
+
server: GovServer | None = None,
|
|
83
|
+
*,
|
|
84
|
+
title: str = "Agent OS Governance API",
|
|
85
|
+
) -> FastAPI:
|
|
86
|
+
"""Build and return the FastAPI application with all routes."""
|
|
87
|
+
from agent_os import __version__
|
|
88
|
+
|
|
89
|
+
version = server._version if server else __version__
|
|
90
|
+
|
|
91
|
+
app = FastAPI(
|
|
92
|
+
title=title,
|
|
93
|
+
version=version,
|
|
94
|
+
description="REST API for Agent OS governance operations.",
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# -- CORS middleware ----------------------------------------------------
|
|
98
|
+
app.add_middleware(
|
|
99
|
+
CORSMiddleware,
|
|
100
|
+
allow_origins=os.environ.get("CORS_ALLOWED_ORIGINS", "http://localhost:3000,http://localhost:8080").split(","),
|
|
101
|
+
allow_credentials=True,
|
|
102
|
+
allow_methods=["*"],
|
|
103
|
+
allow_headers=["*"],
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# -- request timing middleware -----------------------------------------
|
|
107
|
+
@app.middleware("http")
|
|
108
|
+
async def _timing_middleware(request: Request, call_next: Any) -> Any:
|
|
109
|
+
start = time.perf_counter()
|
|
110
|
+
response = await call_next(request)
|
|
111
|
+
elapsed_ms = (time.perf_counter() - start) * 1000.0
|
|
112
|
+
response.headers["X-Response-Time"] = f"{elapsed_ms:.2f}ms"
|
|
113
|
+
return response
|
|
114
|
+
|
|
115
|
+
# -- exception handler -------------------------------------------------
|
|
116
|
+
@app.exception_handler(Exception)
|
|
117
|
+
async def _global_exception_handler(request: Request, exc: Exception) -> JSONResponse:
|
|
118
|
+
logger.exception("Unhandled exception: %s", exc)
|
|
119
|
+
return JSONResponse(
|
|
120
|
+
status_code=500,
|
|
121
|
+
content=ErrorResponse(
|
|
122
|
+
detail=str(exc),
|
|
123
|
+
error_code="INTERNAL_ERROR",
|
|
124
|
+
).model_dump(),
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# ======================================================================
|
|
128
|
+
# Routes
|
|
129
|
+
# ======================================================================
|
|
130
|
+
|
|
131
|
+
@app.get("/")
|
|
132
|
+
async def root() -> dict:
|
|
133
|
+
"""Root info endpoint."""
|
|
134
|
+
return {
|
|
135
|
+
"name": "Agent OS Governance API",
|
|
136
|
+
"version": version,
|
|
137
|
+
"docs": "/docs",
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# -- health / readiness ------------------------------------------------
|
|
141
|
+
|
|
142
|
+
@app.get("/health", response_model=HealthResponse)
|
|
143
|
+
async def health() -> HealthResponse:
|
|
144
|
+
"""Health check endpoint."""
|
|
145
|
+
if server:
|
|
146
|
+
report = server.health_checker.check_health()
|
|
147
|
+
return HealthResponse(
|
|
148
|
+
status=report.status.value,
|
|
149
|
+
components={
|
|
150
|
+
name: {
|
|
151
|
+
"status": comp.status.value,
|
|
152
|
+
"message": comp.message,
|
|
153
|
+
}
|
|
154
|
+
for name, comp in report.components.items()
|
|
155
|
+
},
|
|
156
|
+
timestamp=report.timestamp,
|
|
157
|
+
)
|
|
158
|
+
return HealthResponse(
|
|
159
|
+
status="healthy",
|
|
160
|
+
components={},
|
|
161
|
+
timestamp=datetime.now(timezone.utc).isoformat() + "Z",
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
@app.get("/ready")
|
|
165
|
+
async def ready() -> dict:
|
|
166
|
+
"""Readiness probe."""
|
|
167
|
+
if server:
|
|
168
|
+
report = server.health_checker.check_ready()
|
|
169
|
+
if not report.is_ready():
|
|
170
|
+
raise HTTPException(status_code=503, detail="Not ready")
|
|
171
|
+
return {"ready": True}
|
|
172
|
+
|
|
173
|
+
# -- metrics -----------------------------------------------------------
|
|
174
|
+
|
|
175
|
+
@app.get("/api/v1/metrics", response_model=MetricsResponse)
|
|
176
|
+
async def get_metrics() -> MetricsResponse:
|
|
177
|
+
"""Return governance metrics snapshot."""
|
|
178
|
+
if server:
|
|
179
|
+
snap = server.metrics.snapshot()
|
|
180
|
+
return MetricsResponse(
|
|
181
|
+
total_checks=snap["total_checks"],
|
|
182
|
+
violations=snap["violations"],
|
|
183
|
+
approvals=snap["approvals"],
|
|
184
|
+
blocked=snap["blocked"],
|
|
185
|
+
avg_latency_ms=snap["avg_latency_ms"],
|
|
186
|
+
)
|
|
187
|
+
return MetricsResponse()
|
|
188
|
+
|
|
189
|
+
# -- prompt injection detection ----------------------------------------
|
|
190
|
+
|
|
191
|
+
@app.post("/api/v1/detect/injection", response_model=DetectionResponse)
|
|
192
|
+
async def detect_injection(req: DetectInjectionRequest) -> DetectionResponse:
|
|
193
|
+
"""Scan a single text for prompt injection."""
|
|
194
|
+
from agent_os.prompt_injection import DetectionConfig, PromptInjectionDetector
|
|
195
|
+
|
|
196
|
+
if server and req.sensitivity == server.detector._config.sensitivity:
|
|
197
|
+
detector = server.detector
|
|
198
|
+
else:
|
|
199
|
+
detector = PromptInjectionDetector(
|
|
200
|
+
DetectionConfig(sensitivity=req.sensitivity)
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
result = detector.detect(req.text, req.source, req.canary_tokens)
|
|
204
|
+
return _detection_result_to_response(result)
|
|
205
|
+
|
|
206
|
+
@app.post("/api/v1/detect/injection/batch", response_model=DetectionBatchResponse)
|
|
207
|
+
async def detect_injection_batch(req: DetectBatchRequest) -> DetectionBatchResponse:
|
|
208
|
+
"""Scan multiple texts for prompt injection."""
|
|
209
|
+
from agent_os.prompt_injection import DetectionConfig, PromptInjectionDetector
|
|
210
|
+
|
|
211
|
+
if server and req.sensitivity == server.detector._config.sensitivity:
|
|
212
|
+
detector = server.detector
|
|
213
|
+
else:
|
|
214
|
+
detector = PromptInjectionDetector(
|
|
215
|
+
DetectionConfig(sensitivity=req.sensitivity)
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
inputs = [(item.get("text", ""), item.get("source", "api")) for item in req.inputs]
|
|
219
|
+
results = detector.detect_batch(inputs, req.canary_tokens)
|
|
220
|
+
responses = [_detection_result_to_response(r) for r in results]
|
|
221
|
+
injections = sum(1 for r in responses if r.is_injection)
|
|
222
|
+
|
|
223
|
+
return DetectionBatchResponse(
|
|
224
|
+
results=responses,
|
|
225
|
+
total=len(responses),
|
|
226
|
+
injections_found=injections,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
# -- execute -----------------------------------------------------------
|
|
230
|
+
|
|
231
|
+
@app.post("/api/v1/execute", response_model=ExecuteResponse)
|
|
232
|
+
async def execute(req: ExecuteRequest) -> ExecuteResponse:
|
|
233
|
+
"""Execute an action through the stateless kernel."""
|
|
234
|
+
from agent_os.stateless import ExecutionContext, StatelessKernel
|
|
235
|
+
|
|
236
|
+
kernel = StatelessKernel()
|
|
237
|
+
ctx = ExecutionContext(
|
|
238
|
+
agent_id=req.agent_id,
|
|
239
|
+
policies=req.policies,
|
|
240
|
+
)
|
|
241
|
+
try:
|
|
242
|
+
result = await kernel.execute(req.action, req.params, ctx)
|
|
243
|
+
return ExecuteResponse(
|
|
244
|
+
success=result.success,
|
|
245
|
+
data=result.data,
|
|
246
|
+
error=result.error,
|
|
247
|
+
signal=result.signal,
|
|
248
|
+
)
|
|
249
|
+
except Exception as exc:
|
|
250
|
+
return ExecuteResponse(
|
|
251
|
+
success=False,
|
|
252
|
+
error=str(exc),
|
|
253
|
+
signal="SIGTERM",
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
# -- audit -------------------------------------------------------------
|
|
257
|
+
|
|
258
|
+
@app.get("/api/v1/audit/injections")
|
|
259
|
+
async def audit_injections(limit: int = Query(default=50, ge=1, le=1000)) -> dict:
|
|
260
|
+
"""Return recent injection audit log entries."""
|
|
261
|
+
records: list[dict] = []
|
|
262
|
+
if server:
|
|
263
|
+
for rec in server.detector.audit_log[-limit:]:
|
|
264
|
+
records.append({
|
|
265
|
+
"timestamp": rec.timestamp.isoformat(),
|
|
266
|
+
"input_hash": rec.input_hash,
|
|
267
|
+
"source": rec.source,
|
|
268
|
+
"is_injection": rec.result.is_injection,
|
|
269
|
+
"threat_level": rec.result.threat_level.value,
|
|
270
|
+
"injection_type": (
|
|
271
|
+
rec.result.injection_type.value if rec.result.injection_type else None
|
|
272
|
+
),
|
|
273
|
+
"explanation": rec.result.explanation,
|
|
274
|
+
})
|
|
275
|
+
return {"records": records, "total": len(records)}
|
|
276
|
+
|
|
277
|
+
return app
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""Pydantic v2 request/response models for Agent OS Governance API."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, Field
|
|
10
|
+
|
|
11
|
+
# ---------------------------------------------------------------------------
|
|
12
|
+
# Request models
|
|
13
|
+
# ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
class ExecuteRequest(BaseModel):
|
|
16
|
+
"""Request to execute an action through the stateless kernel."""
|
|
17
|
+
|
|
18
|
+
action: str = Field(..., description="Action to execute")
|
|
19
|
+
params: dict = Field(default_factory=dict, description="Action parameters")
|
|
20
|
+
agent_id: str = Field(..., description="Unique agent identifier")
|
|
21
|
+
policies: list[str] = Field(default_factory=list, description="Policy names to enforce")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class DetectInjectionRequest(BaseModel):
|
|
25
|
+
"""Request to scan a single text for prompt injection."""
|
|
26
|
+
|
|
27
|
+
text: str = Field(..., description="Input text to screen")
|
|
28
|
+
source: str = Field(default="api", description="Source identifier")
|
|
29
|
+
canary_tokens: list[str] | None = Field(default=None, description="Canary tokens to check")
|
|
30
|
+
sensitivity: str = Field(default="balanced", description="Detection sensitivity")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class DetectBatchRequest(BaseModel):
|
|
34
|
+
"""Request to scan multiple texts for prompt injection."""
|
|
35
|
+
|
|
36
|
+
inputs: list[dict] = Field(
|
|
37
|
+
..., description="List of dicts with 'text' and optional 'source' keys"
|
|
38
|
+
)
|
|
39
|
+
canary_tokens: list[str] | None = Field(default=None, description="Canary tokens to check")
|
|
40
|
+
sensitivity: str = Field(default="balanced", description="Detection sensitivity")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class PolicyEvalRequest(BaseModel):
|
|
44
|
+
"""Request to evaluate policies against a context."""
|
|
45
|
+
|
|
46
|
+
context: dict = Field(..., description="Execution context for policy evaluation")
|
|
47
|
+
policy_name: str | None = Field(default=None, description="Specific policy to evaluate")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
# Response models
|
|
52
|
+
# ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
class ExecuteResponse(BaseModel):
|
|
55
|
+
"""Response from stateless kernel execution."""
|
|
56
|
+
|
|
57
|
+
success: bool
|
|
58
|
+
data: Any = None
|
|
59
|
+
error: str | None = None
|
|
60
|
+
signal: str | None = None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class DetectionResponse(BaseModel):
|
|
64
|
+
"""Response from prompt injection detection."""
|
|
65
|
+
|
|
66
|
+
is_injection: bool
|
|
67
|
+
threat_level: str
|
|
68
|
+
injection_type: str | None = None
|
|
69
|
+
confidence: float
|
|
70
|
+
matched_patterns: list[str] = Field(default_factory=list)
|
|
71
|
+
explanation: str = ""
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class DetectionBatchResponse(BaseModel):
|
|
75
|
+
"""Response from batch prompt injection detection."""
|
|
76
|
+
|
|
77
|
+
results: list[DetectionResponse]
|
|
78
|
+
total: int
|
|
79
|
+
injections_found: int
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class HealthResponse(BaseModel):
|
|
83
|
+
"""Health check response."""
|
|
84
|
+
|
|
85
|
+
status: str
|
|
86
|
+
components: dict = Field(default_factory=dict)
|
|
87
|
+
timestamp: str = ""
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class MetricsResponse(BaseModel):
|
|
91
|
+
"""Governance metrics snapshot."""
|
|
92
|
+
|
|
93
|
+
total_checks: int = 0
|
|
94
|
+
violations: int = 0
|
|
95
|
+
approvals: int = 0
|
|
96
|
+
blocked: int = 0
|
|
97
|
+
avg_latency_ms: float = 0.0
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class ErrorResponse(BaseModel):
|
|
101
|
+
"""Error response for failed requests."""
|
|
102
|
+
|
|
103
|
+
detail: str
|
|
104
|
+
error_code: str = "INTERNAL_ERROR"
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""Shift-left compliance metrics.
|
|
4
|
+
|
|
5
|
+
Tracks where policy violations are caught in the development lifecycle
|
|
6
|
+
and computes a shift-left score: the earlier violations are detected,
|
|
7
|
+
the higher the score.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from datetime import datetime, timezone
|
|
13
|
+
from enum import Enum
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ViolationStage(str, Enum):
|
|
18
|
+
"""Stage in the development lifecycle where a violation was caught."""
|
|
19
|
+
|
|
20
|
+
PRE_COMMIT = "pre_commit" # IDE / local hooks
|
|
21
|
+
PR_CHECK = "pr_check" # Pull-request checks
|
|
22
|
+
CI_GATE = "ci_gate" # CI/CD pipeline gates
|
|
23
|
+
RUNTIME = "runtime" # Production / agent runtime
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Weights: earlier stages earn a higher shift-left contribution.
|
|
27
|
+
_STAGE_WEIGHTS: dict[ViolationStage, float] = {
|
|
28
|
+
ViolationStage.PRE_COMMIT: 1.0,
|
|
29
|
+
ViolationStage.PR_CHECK: 0.75,
|
|
30
|
+
ViolationStage.CI_GATE: 0.5,
|
|
31
|
+
ViolationStage.RUNTIME: 0.0,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class ViolationRecord:
|
|
37
|
+
"""A single recorded policy violation."""
|
|
38
|
+
|
|
39
|
+
rule_name: str
|
|
40
|
+
stage: ViolationStage
|
|
41
|
+
timestamp: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
|
|
42
|
+
resolved: bool = False
|
|
43
|
+
message: str = ""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ShiftLeftTracker:
|
|
47
|
+
"""Tracks violation records and computes shift-left metrics."""
|
|
48
|
+
|
|
49
|
+
def __init__(self) -> None:
|
|
50
|
+
self._records: list[ViolationRecord] = []
|
|
51
|
+
|
|
52
|
+
def record(
|
|
53
|
+
self,
|
|
54
|
+
rule_name: str,
|
|
55
|
+
stage: ViolationStage,
|
|
56
|
+
*,
|
|
57
|
+
resolved: bool = False,
|
|
58
|
+
message: str = "",
|
|
59
|
+
) -> ViolationRecord:
|
|
60
|
+
"""Record a new violation and return the record."""
|
|
61
|
+
rec = ViolationRecord(
|
|
62
|
+
rule_name=rule_name,
|
|
63
|
+
stage=stage,
|
|
64
|
+
resolved=resolved,
|
|
65
|
+
message=message,
|
|
66
|
+
)
|
|
67
|
+
self._records.append(rec)
|
|
68
|
+
return rec
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def records(self) -> list[ViolationRecord]:
|
|
72
|
+
return list(self._records)
|
|
73
|
+
|
|
74
|
+
# ------------------------------------------------------------------
|
|
75
|
+
# Stage distribution
|
|
76
|
+
# ------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
def stage_distribution(self) -> dict[str, int]:
|
|
79
|
+
"""Return counts of violations per stage."""
|
|
80
|
+
dist: dict[str, int] = {s.value: 0 for s in ViolationStage}
|
|
81
|
+
for rec in self._records:
|
|
82
|
+
dist[rec.stage.value] += 1
|
|
83
|
+
return dist
|
|
84
|
+
|
|
85
|
+
# ------------------------------------------------------------------
|
|
86
|
+
# Shift-left score
|
|
87
|
+
# ------------------------------------------------------------------
|
|
88
|
+
|
|
89
|
+
def shift_left_score(self) -> float:
|
|
90
|
+
"""Compute the shift-left score (0.0 - 1.0).
|
|
91
|
+
|
|
92
|
+
A score of 1.0 means every violation was caught at *pre_commit*.
|
|
93
|
+
A score of 0.0 means every violation was only caught at *runtime*
|
|
94
|
+
(or no violations were recorded).
|
|
95
|
+
"""
|
|
96
|
+
if not self._records:
|
|
97
|
+
return 0.0
|
|
98
|
+
total_weight = sum(
|
|
99
|
+
_STAGE_WEIGHTS[rec.stage] for rec in self._records
|
|
100
|
+
)
|
|
101
|
+
return total_weight / len(self._records)
|
|
102
|
+
|
|
103
|
+
# ------------------------------------------------------------------
|
|
104
|
+
# Trend report
|
|
105
|
+
# ------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
def trend_report(self) -> dict[str, Any]:
|
|
108
|
+
"""Generate a summary trend report."""
|
|
109
|
+
total = len(self._records)
|
|
110
|
+
resolved = sum(1 for r in self._records if r.resolved)
|
|
111
|
+
return {
|
|
112
|
+
"total_violations": total,
|
|
113
|
+
"resolved": resolved,
|
|
114
|
+
"unresolved": total - resolved,
|
|
115
|
+
"shift_left_score": round(self.shift_left_score(), 3),
|
|
116
|
+
"stage_distribution": self.stage_distribution(),
|
|
117
|
+
"resolution_rate": round(resolved / total, 3) if total else 0.0,
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
# ------------------------------------------------------------------
|
|
121
|
+
# Helpers
|
|
122
|
+
# ------------------------------------------------------------------
|
|
123
|
+
|
|
124
|
+
def violations_for_rule(self, rule_name: str) -> list[ViolationRecord]:
|
|
125
|
+
"""Return all violation records for a given rule name."""
|
|
126
|
+
return [r for r in self._records if r.rule_name == rule_name]
|
|
127
|
+
|
|
128
|
+
def clear(self) -> None:
|
|
129
|
+
"""Remove all recorded violations."""
|
|
130
|
+
self._records.clear()
|