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,420 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""
|
|
4
|
+
Anthropic Claude Integration
|
|
5
|
+
|
|
6
|
+
Wraps Anthropic's Messages API with Agent OS governance.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
from agent_os.integrations.anthropic_adapter import AnthropicKernel
|
|
10
|
+
|
|
11
|
+
kernel = AnthropicKernel(policy=GovernancePolicy(
|
|
12
|
+
max_tokens=4096,
|
|
13
|
+
allowed_tools=["web_search", "code_interpreter"],
|
|
14
|
+
blocked_patterns=["password", "api_key"],
|
|
15
|
+
))
|
|
16
|
+
|
|
17
|
+
governed = kernel.wrap(client)
|
|
18
|
+
# All messages.create() calls are now governed
|
|
19
|
+
response = governed.messages.create(
|
|
20
|
+
model="claude-sonnet-4-20250514",
|
|
21
|
+
max_tokens=1024,
|
|
22
|
+
messages=[{"role": "user", "content": "Hello"}],
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
Features:
|
|
26
|
+
- Pre-execution policy checks on message content
|
|
27
|
+
- Tool call interception and validation
|
|
28
|
+
- Token limit enforcement
|
|
29
|
+
- Content filtering via blocked patterns
|
|
30
|
+
- SIGKILL support (cancel running requests)
|
|
31
|
+
- Full audit trail
|
|
32
|
+
- Health check endpoint
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
from __future__ import annotations
|
|
36
|
+
|
|
37
|
+
import logging
|
|
38
|
+
import time
|
|
39
|
+
from dataclasses import dataclass, field
|
|
40
|
+
from datetime import datetime
|
|
41
|
+
from typing import Any
|
|
42
|
+
|
|
43
|
+
from .base import BaseIntegration, ExecutionContext, GovernancePolicy
|
|
44
|
+
|
|
45
|
+
logger = logging.getLogger("agent_os.anthropic")
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
import anthropic as _anthropic_mod # noqa: F401
|
|
49
|
+
|
|
50
|
+
_HAS_ANTHROPIC = True
|
|
51
|
+
except ImportError:
|
|
52
|
+
_HAS_ANTHROPIC = False
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _check_anthropic_available() -> None:
|
|
56
|
+
"""Raise a helpful error when the ``anthropic`` package is missing."""
|
|
57
|
+
if not _HAS_ANTHROPIC:
|
|
58
|
+
raise ImportError(
|
|
59
|
+
"The 'anthropic' package is required for AnthropicKernel. "
|
|
60
|
+
"Install it with: pip install anthropic"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass
|
|
65
|
+
class AnthropicContext(ExecutionContext):
|
|
66
|
+
"""Execution context for Anthropic Claude interactions.
|
|
67
|
+
|
|
68
|
+
Attributes:
|
|
69
|
+
model: The model used for this session.
|
|
70
|
+
message_ids: Recorded message response IDs.
|
|
71
|
+
tool_use_calls: History of tool-use blocks returned by Claude.
|
|
72
|
+
prompt_tokens: Cumulative input tokens consumed.
|
|
73
|
+
completion_tokens: Cumulative output tokens consumed.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
model: str = ""
|
|
77
|
+
message_ids: list[str] = field(default_factory=list)
|
|
78
|
+
tool_use_calls: list[dict[str, Any]] = field(default_factory=list)
|
|
79
|
+
prompt_tokens: int = 0
|
|
80
|
+
completion_tokens: int = 0
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class PolicyViolationError(Exception):
|
|
84
|
+
"""Raised when a Claude request violates governance policy."""
|
|
85
|
+
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class RequestCancelledException(Exception):
|
|
90
|
+
"""Raised when a request is cancelled via SIGKILL."""
|
|
91
|
+
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class AnthropicKernel(BaseIntegration):
|
|
96
|
+
"""Anthropic Claude adapter for Agent OS.
|
|
97
|
+
|
|
98
|
+
Provides governance for the Anthropic Messages API including policy
|
|
99
|
+
enforcement, tool-call validation, token tracking, and audit logging.
|
|
100
|
+
|
|
101
|
+
Example:
|
|
102
|
+
>>> kernel = AnthropicKernel(policy=GovernancePolicy(max_tokens=8192))
|
|
103
|
+
>>> governed = kernel.wrap(anthropic.Anthropic())
|
|
104
|
+
>>> response = governed.messages.create(
|
|
105
|
+
... model="claude-sonnet-4-20250514",
|
|
106
|
+
... max_tokens=1024,
|
|
107
|
+
... messages=[{"role": "user", "content": "Hello"}],
|
|
108
|
+
... )
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
def __init__(
|
|
112
|
+
self,
|
|
113
|
+
policy: GovernancePolicy | None = None,
|
|
114
|
+
max_retries: int = 3,
|
|
115
|
+
timeout_seconds: float = 300.0,
|
|
116
|
+
) -> None:
|
|
117
|
+
"""Initialise the Anthropic governance kernel.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
policy: Governance policy to enforce. Uses default when ``None``.
|
|
121
|
+
max_retries: Maximum retry attempts for transient errors.
|
|
122
|
+
timeout_seconds: Default timeout for operations.
|
|
123
|
+
"""
|
|
124
|
+
super().__init__(policy)
|
|
125
|
+
self.max_retries = max_retries
|
|
126
|
+
self.timeout_seconds = timeout_seconds
|
|
127
|
+
self._wrapped_clients: dict[int, Any] = {}
|
|
128
|
+
self._cancelled_requests: set[str] = set()
|
|
129
|
+
self._start_time = time.monotonic()
|
|
130
|
+
self._last_error: str | None = None
|
|
131
|
+
|
|
132
|
+
def wrap(self, client: Any) -> GovernedAnthropicClient:
|
|
133
|
+
"""Wrap an Anthropic client with governance.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
client: An ``anthropic.Anthropic`` client instance.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
A ``GovernedAnthropicClient`` that enforces policy on all
|
|
140
|
+
``messages.create()`` calls.
|
|
141
|
+
"""
|
|
142
|
+
_check_anthropic_available()
|
|
143
|
+
client_id = id(client)
|
|
144
|
+
ctx = AnthropicContext(
|
|
145
|
+
agent_id=f"anthropic-{client_id}",
|
|
146
|
+
session_id=f"ant-{int(time.time())}",
|
|
147
|
+
policy=self.policy,
|
|
148
|
+
)
|
|
149
|
+
self.contexts[ctx.agent_id] = ctx
|
|
150
|
+
self._wrapped_clients[client_id] = client
|
|
151
|
+
|
|
152
|
+
return GovernedAnthropicClient(
|
|
153
|
+
client=client,
|
|
154
|
+
kernel=self,
|
|
155
|
+
ctx=ctx,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
def unwrap(self, governed_agent: Any) -> Any:
|
|
159
|
+
"""Retrieve the original unwrapped Anthropic client.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
governed_agent: A ``GovernedAnthropicClient`` or any object.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
The original Anthropic client if applicable, otherwise
|
|
166
|
+
*governed_agent* as-is.
|
|
167
|
+
"""
|
|
168
|
+
if isinstance(governed_agent, GovernedAnthropicClient):
|
|
169
|
+
return governed_agent._client
|
|
170
|
+
return governed_agent
|
|
171
|
+
|
|
172
|
+
def cancel_request(self, request_id: str) -> None:
|
|
173
|
+
"""Cancel a request (SIGKILL equivalent).
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
request_id: Identifier of the request to cancel.
|
|
177
|
+
"""
|
|
178
|
+
self._cancelled_requests.add(request_id)
|
|
179
|
+
logger.info("Request %s marked for cancellation", request_id)
|
|
180
|
+
|
|
181
|
+
def is_cancelled(self, request_id: str) -> bool:
|
|
182
|
+
"""Check whether a request has been cancelled.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
request_id: The request identifier to check.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
``True`` if the request was previously cancelled.
|
|
189
|
+
"""
|
|
190
|
+
return request_id in self._cancelled_requests
|
|
191
|
+
|
|
192
|
+
def health_check(self) -> dict[str, Any]:
|
|
193
|
+
"""Return adapter health status.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
A dict with ``status``, ``backend``, ``last_error``, and
|
|
197
|
+
``uptime_seconds`` keys.
|
|
198
|
+
"""
|
|
199
|
+
uptime = time.monotonic() - self._start_time
|
|
200
|
+
has_clients = bool(self._wrapped_clients)
|
|
201
|
+
status = "degraded" if self._last_error else "healthy"
|
|
202
|
+
return {
|
|
203
|
+
"status": status,
|
|
204
|
+
"backend": "anthropic",
|
|
205
|
+
"backend_connected": has_clients,
|
|
206
|
+
"last_error": self._last_error,
|
|
207
|
+
"uptime_seconds": round(uptime, 2),
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class _GovernedMessages:
|
|
212
|
+
"""Proxy for ``client.messages`` that intercepts ``create()``."""
|
|
213
|
+
|
|
214
|
+
def __init__(
|
|
215
|
+
self,
|
|
216
|
+
client: Any,
|
|
217
|
+
kernel: AnthropicKernel,
|
|
218
|
+
ctx: AnthropicContext,
|
|
219
|
+
) -> None:
|
|
220
|
+
self._client = client
|
|
221
|
+
self._kernel = kernel
|
|
222
|
+
self._ctx = ctx
|
|
223
|
+
|
|
224
|
+
def create(self, **kwargs: Any) -> Any:
|
|
225
|
+
"""Create a message with governance enforcement.
|
|
226
|
+
|
|
227
|
+
Validates message content against blocked patterns, enforces
|
|
228
|
+
tool-call allowlists, checks token limits after completion,
|
|
229
|
+
and records an audit trail.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
**kwargs: Forwarded to ``client.messages.create()``.
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
The Anthropic message response.
|
|
236
|
+
|
|
237
|
+
Raises:
|
|
238
|
+
PolicyViolationError: If a governance policy is violated.
|
|
239
|
+
RequestCancelledException: If the request was SIGKILL'd.
|
|
240
|
+
"""
|
|
241
|
+
# --- pre-execution checks ---
|
|
242
|
+
messages = kwargs.get("messages", [])
|
|
243
|
+
for msg in messages:
|
|
244
|
+
content = msg.get("content", "") if isinstance(msg, dict) else str(msg)
|
|
245
|
+
allowed, reason = self._kernel.pre_execute(self._ctx, content)
|
|
246
|
+
if not allowed:
|
|
247
|
+
raise PolicyViolationError(f"Message blocked: {reason}")
|
|
248
|
+
|
|
249
|
+
# Validate requested tools against policy
|
|
250
|
+
tools = kwargs.get("tools")
|
|
251
|
+
if tools:
|
|
252
|
+
self._validate_tools(tools)
|
|
253
|
+
|
|
254
|
+
# Enforce max_tokens cap from policy
|
|
255
|
+
requested_max = kwargs.get("max_tokens", 0)
|
|
256
|
+
if requested_max > self._kernel.policy.max_tokens:
|
|
257
|
+
raise PolicyViolationError(
|
|
258
|
+
f"Requested max_tokens ({requested_max}) exceeds policy limit "
|
|
259
|
+
f"({self._kernel.policy.max_tokens})"
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
# Audit log
|
|
263
|
+
logger.info(
|
|
264
|
+
"Anthropic messages.create | agent=%s model=%s",
|
|
265
|
+
self._ctx.agent_id,
|
|
266
|
+
kwargs.get("model", "unknown"),
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
# --- execute ---
|
|
270
|
+
try:
|
|
271
|
+
response = self._client.messages.create(**kwargs)
|
|
272
|
+
except Exception as exc:
|
|
273
|
+
self._kernel._last_error = str(exc)
|
|
274
|
+
raise
|
|
275
|
+
|
|
276
|
+
# --- post-execution checks ---
|
|
277
|
+
response_id = getattr(response, "id", f"msg-{int(time.time())}")
|
|
278
|
+
self._ctx.message_ids.append(response_id)
|
|
279
|
+
|
|
280
|
+
if self._kernel.is_cancelled(response_id):
|
|
281
|
+
raise RequestCancelledException("Request was cancelled (SIGKILL)")
|
|
282
|
+
|
|
283
|
+
# Track tokens
|
|
284
|
+
usage = getattr(response, "usage", None)
|
|
285
|
+
if usage:
|
|
286
|
+
self._ctx.prompt_tokens += getattr(usage, "input_tokens", 0)
|
|
287
|
+
self._ctx.completion_tokens += getattr(usage, "output_tokens", 0)
|
|
288
|
+
|
|
289
|
+
total = self._ctx.prompt_tokens + self._ctx.completion_tokens
|
|
290
|
+
if total > self._kernel.policy.max_tokens:
|
|
291
|
+
raise PolicyViolationError(
|
|
292
|
+
f"Token limit exceeded: {total} > {self._kernel.policy.max_tokens}"
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
# Validate tool_use blocks in response
|
|
296
|
+
content_blocks = getattr(response, "content", [])
|
|
297
|
+
for block in content_blocks:
|
|
298
|
+
if getattr(block, "type", None) == "tool_use":
|
|
299
|
+
tool_name = getattr(block, "name", "")
|
|
300
|
+
call_info = {
|
|
301
|
+
"id": getattr(block, "id", ""),
|
|
302
|
+
"name": tool_name,
|
|
303
|
+
"input": getattr(block, "input", {}),
|
|
304
|
+
"timestamp": datetime.now().isoformat(),
|
|
305
|
+
}
|
|
306
|
+
self._ctx.tool_use_calls.append(call_info)
|
|
307
|
+
self._ctx.tool_calls.append(call_info)
|
|
308
|
+
|
|
309
|
+
if len(self._ctx.tool_calls) > self._kernel.policy.max_tool_calls:
|
|
310
|
+
raise PolicyViolationError(
|
|
311
|
+
f"Tool call limit exceeded: "
|
|
312
|
+
f"{len(self._ctx.tool_calls)} > "
|
|
313
|
+
f"{self._kernel.policy.max_tool_calls}"
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
if self._kernel.policy.allowed_tools:
|
|
317
|
+
if tool_name not in self._kernel.policy.allowed_tools:
|
|
318
|
+
raise PolicyViolationError(
|
|
319
|
+
f"Tool not allowed: {tool_name}"
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
if self._kernel.policy.require_human_approval:
|
|
323
|
+
raise PolicyViolationError(
|
|
324
|
+
f"Tool '{tool_name}' requires human approval per governance policy"
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
# Post-execute bookkeeping
|
|
328
|
+
self._kernel.post_execute(self._ctx, response)
|
|
329
|
+
|
|
330
|
+
return response
|
|
331
|
+
|
|
332
|
+
def _validate_tools(self, tools: list[Any]) -> None:
|
|
333
|
+
"""Validate tool definitions against policy allowlist.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
tools: List of tool definitions from the request.
|
|
337
|
+
|
|
338
|
+
Raises:
|
|
339
|
+
PolicyViolationError: If a tool is not in the allowed list.
|
|
340
|
+
"""
|
|
341
|
+
if not self._kernel.policy.allowed_tools:
|
|
342
|
+
return
|
|
343
|
+
for tool in tools:
|
|
344
|
+
name = tool.get("name") if isinstance(tool, dict) else getattr(tool, "name", None)
|
|
345
|
+
if name and name not in self._kernel.policy.allowed_tools:
|
|
346
|
+
raise PolicyViolationError(f"Tool not allowed: {name}")
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
class GovernedAnthropicClient:
|
|
350
|
+
"""Anthropic client wrapped with Agent OS governance.
|
|
351
|
+
|
|
352
|
+
Transparently proxies attribute access to the underlying client
|
|
353
|
+
while intercepting ``messages.create()`` for policy enforcement.
|
|
354
|
+
"""
|
|
355
|
+
|
|
356
|
+
def __init__(
|
|
357
|
+
self,
|
|
358
|
+
client: Any,
|
|
359
|
+
kernel: AnthropicKernel,
|
|
360
|
+
ctx: AnthropicContext,
|
|
361
|
+
) -> None:
|
|
362
|
+
self._client = client
|
|
363
|
+
self._kernel = kernel
|
|
364
|
+
self._ctx = ctx
|
|
365
|
+
self.messages = _GovernedMessages(client, kernel, ctx)
|
|
366
|
+
|
|
367
|
+
def sigkill(self, request_id: str) -> None:
|
|
368
|
+
"""Send SIGKILL — immediately cancel a request.
|
|
369
|
+
|
|
370
|
+
Args:
|
|
371
|
+
request_id: The message ID to cancel.
|
|
372
|
+
"""
|
|
373
|
+
self._kernel.cancel_request(request_id)
|
|
374
|
+
|
|
375
|
+
def get_context(self) -> AnthropicContext:
|
|
376
|
+
"""Return the execution context with the full audit trail.
|
|
377
|
+
|
|
378
|
+
Returns:
|
|
379
|
+
The ``AnthropicContext`` for this governed client.
|
|
380
|
+
"""
|
|
381
|
+
return self._ctx
|
|
382
|
+
|
|
383
|
+
def get_token_usage(self) -> dict[str, Any]:
|
|
384
|
+
"""Return cumulative token usage statistics.
|
|
385
|
+
|
|
386
|
+
Returns:
|
|
387
|
+
A dict with ``prompt_tokens``, ``completion_tokens``,
|
|
388
|
+
``total_tokens``, and ``limit``.
|
|
389
|
+
"""
|
|
390
|
+
return {
|
|
391
|
+
"prompt_tokens": self._ctx.prompt_tokens,
|
|
392
|
+
"completion_tokens": self._ctx.completion_tokens,
|
|
393
|
+
"total_tokens": self._ctx.prompt_tokens + self._ctx.completion_tokens,
|
|
394
|
+
"limit": self._kernel.policy.max_tokens,
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
def __getattr__(self, name: str) -> Any:
|
|
398
|
+
"""Proxy attribute access to the underlying Anthropic client."""
|
|
399
|
+
return getattr(self._client, name)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def wrap_client(
|
|
403
|
+
client: Any,
|
|
404
|
+
policy: GovernancePolicy | None = None,
|
|
405
|
+
) -> GovernedAnthropicClient:
|
|
406
|
+
"""Quick wrapper for Anthropic clients.
|
|
407
|
+
|
|
408
|
+
Args:
|
|
409
|
+
client: An ``anthropic.Anthropic`` client instance.
|
|
410
|
+
policy: Optional governance policy.
|
|
411
|
+
|
|
412
|
+
Returns:
|
|
413
|
+
A governed client.
|
|
414
|
+
|
|
415
|
+
Example:
|
|
416
|
+
>>> from agent_os.integrations.anthropic_adapter import wrap_client
|
|
417
|
+
>>> governed = wrap_client(my_client)
|
|
418
|
+
>>> response = governed.messages.create(model="claude-sonnet-4-20250514", ...)
|
|
419
|
+
"""
|
|
420
|
+
return AnthropicKernel(policy=policy).wrap(client)
|