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,394 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Agent Hibernation - State-to-Disk Serialization
|
|
6
|
+
|
|
7
|
+
Feature: "Agent Hibernation (state-to-disk)"
|
|
8
|
+
Problem: Agents sitting idle in memory cost money (if hosted) or RAM.
|
|
9
|
+
Solution: Serialize the entire agent state (including caas pointer) to a JSON/Pickle file
|
|
10
|
+
and kill the process. Wake it up only when amb receives a message for it.
|
|
11
|
+
Result: Scale by Subtraction - Removes the need for "always-on" servers. Serverless Agents.
|
|
12
|
+
|
|
13
|
+
This module provides the infrastructure to hibernate idle agents by serializing
|
|
14
|
+
their complete state to disk and restoring them when needed.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from typing import Any, Dict, Optional, List
|
|
18
|
+
from dataclasses import dataclass, field, asdict
|
|
19
|
+
from datetime import datetime, timedelta
|
|
20
|
+
from enum import Enum
|
|
21
|
+
import json
|
|
22
|
+
import hmac
|
|
23
|
+
import hashlib
|
|
24
|
+
import os
|
|
25
|
+
import logging
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class HibernationFormat(Enum):
|
|
30
|
+
"""Format for serializing agent state"""
|
|
31
|
+
JSON = "json"
|
|
32
|
+
PICKLE = "pickle"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class AgentState(Enum):
|
|
36
|
+
"""State of an agent in the hibernation lifecycle"""
|
|
37
|
+
ACTIVE = "active"
|
|
38
|
+
HIBERNATING = "hibernating"
|
|
39
|
+
HIBERNATED = "hibernated"
|
|
40
|
+
WAKING = "waking"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class HibernatedAgentMetadata:
|
|
45
|
+
"""Metadata about a hibernated agent"""
|
|
46
|
+
agent_id: str
|
|
47
|
+
session_id: str
|
|
48
|
+
hibernated_at: datetime
|
|
49
|
+
state_file_path: str
|
|
50
|
+
format: HibernationFormat
|
|
51
|
+
state_size_bytes: int
|
|
52
|
+
last_activity: datetime
|
|
53
|
+
context_pointer: Optional[str] = None # caas pointer
|
|
54
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class HibernationConfig:
|
|
59
|
+
"""Configuration for agent hibernation"""
|
|
60
|
+
enabled: bool = True
|
|
61
|
+
idle_timeout_seconds: int = 300 # 5 minutes default
|
|
62
|
+
storage_path: str = "/tmp/agent_hibernation"
|
|
63
|
+
format: HibernationFormat = HibernationFormat.JSON
|
|
64
|
+
max_hibernated_agents: int = 1000
|
|
65
|
+
auto_cleanup_days: int = 7 # Clean up hibernated agents after 7 days
|
|
66
|
+
compress: bool = False
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class HibernationManager:
|
|
70
|
+
"""
|
|
71
|
+
Manages agent hibernation and wake-up operations.
|
|
72
|
+
|
|
73
|
+
This class handles:
|
|
74
|
+
- Serialization of complete agent state to disk
|
|
75
|
+
- Deserialization and restoration of agent state
|
|
76
|
+
- Tracking of hibernated agents
|
|
77
|
+
- Automatic cleanup of old hibernated states
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def __init__(self, config: Optional[HibernationConfig] = None):
|
|
81
|
+
"""
|
|
82
|
+
Initialize the hibernation manager.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
config: Configuration for hibernation behavior
|
|
86
|
+
"""
|
|
87
|
+
self.config = config or HibernationConfig()
|
|
88
|
+
self.logger = logging.getLogger("HibernationManager")
|
|
89
|
+
|
|
90
|
+
# Create storage directory if it doesn't exist
|
|
91
|
+
Path(self.config.storage_path).mkdir(parents=True, exist_ok=True)
|
|
92
|
+
|
|
93
|
+
# HMAC key for pickle integrity verification — generated per instance.
|
|
94
|
+
# In production, this should be persisted securely (e.g., Key Vault).
|
|
95
|
+
self._hmac_key = os.urandom(32)
|
|
96
|
+
|
|
97
|
+
# Create storage directory if it doesn't exist
|
|
98
|
+
Path(self.config.storage_path).mkdir(parents=True, exist_ok=True)
|
|
99
|
+
|
|
100
|
+
# Track hibernated agents
|
|
101
|
+
self.hibernated_agents: Dict[str, HibernatedAgentMetadata] = {}
|
|
102
|
+
|
|
103
|
+
# Track agent activity for idle detection
|
|
104
|
+
self.agent_activity: Dict[str, datetime] = {}
|
|
105
|
+
|
|
106
|
+
# Track agent states
|
|
107
|
+
self.agent_states: Dict[str, AgentState] = {}
|
|
108
|
+
|
|
109
|
+
self.logger.info(f"HibernationManager initialized with storage at {self.config.storage_path}")
|
|
110
|
+
|
|
111
|
+
def serialize_agent_state(
|
|
112
|
+
self,
|
|
113
|
+
agent_id: str,
|
|
114
|
+
agent_context: Any,
|
|
115
|
+
caas_pointer: Optional[str] = None,
|
|
116
|
+
additional_state: Optional[Dict[str, Any]] = None
|
|
117
|
+
) -> Dict[str, Any]:
|
|
118
|
+
"""
|
|
119
|
+
Serialize agent state to a dictionary.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
agent_id: Unique identifier for the agent
|
|
123
|
+
agent_context: AgentContext object containing agent session data
|
|
124
|
+
caas_pointer: Optional pointer to context in caas (Context-as-a-Service)
|
|
125
|
+
additional_state: Optional additional state to serialize
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
Dictionary containing serialized agent state
|
|
129
|
+
"""
|
|
130
|
+
state = {
|
|
131
|
+
"agent_id": agent_id,
|
|
132
|
+
"session_id": agent_context.session_id,
|
|
133
|
+
"created_at": agent_context.created_at.isoformat(),
|
|
134
|
+
"permissions": {
|
|
135
|
+
str(k): v.value for k, v in agent_context.permissions.items()
|
|
136
|
+
},
|
|
137
|
+
"metadata": agent_context.metadata,
|
|
138
|
+
"caas_pointer": caas_pointer,
|
|
139
|
+
"hibernated_at": datetime.now().isoformat(),
|
|
140
|
+
"additional_state": additional_state or {}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return state
|
|
144
|
+
|
|
145
|
+
def deserialize_agent_state(self, state: Dict[str, Any]) -> Dict[str, Any]:
|
|
146
|
+
"""
|
|
147
|
+
Deserialize agent state from a dictionary.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
state: Dictionary containing serialized agent state
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Dictionary with deserialized state components
|
|
154
|
+
"""
|
|
155
|
+
# Convert ISO format strings back to datetime
|
|
156
|
+
if "created_at" in state:
|
|
157
|
+
state["created_at"] = datetime.fromisoformat(state["created_at"])
|
|
158
|
+
if "hibernated_at" in state:
|
|
159
|
+
state["hibernated_at"] = datetime.fromisoformat(state["hibernated_at"])
|
|
160
|
+
|
|
161
|
+
return state
|
|
162
|
+
|
|
163
|
+
def hibernate_agent(
|
|
164
|
+
self,
|
|
165
|
+
agent_id: str,
|
|
166
|
+
agent_context: Any,
|
|
167
|
+
caas_pointer: Optional[str] = None,
|
|
168
|
+
additional_state: Optional[Dict[str, Any]] = None
|
|
169
|
+
) -> HibernatedAgentMetadata:
|
|
170
|
+
"""
|
|
171
|
+
Hibernate an agent by saving its state to disk.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
agent_id: Unique identifier for the agent
|
|
175
|
+
agent_context: AgentContext object to serialize
|
|
176
|
+
caas_pointer: Optional pointer to context in caas
|
|
177
|
+
additional_state: Optional additional state to save
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
Metadata about the hibernated agent
|
|
181
|
+
"""
|
|
182
|
+
self.logger.info(f"Hibernating agent {agent_id}")
|
|
183
|
+
|
|
184
|
+
# Update agent state
|
|
185
|
+
self.agent_states[agent_id] = AgentState.HIBERNATING
|
|
186
|
+
|
|
187
|
+
# Serialize agent state
|
|
188
|
+
state = self.serialize_agent_state(agent_id, agent_context, caas_pointer, additional_state)
|
|
189
|
+
|
|
190
|
+
# Determine file path
|
|
191
|
+
file_name = f"{agent_id}_{agent_context.session_id}.{self.config.format.value}"
|
|
192
|
+
file_path = os.path.join(self.config.storage_path, file_name)
|
|
193
|
+
|
|
194
|
+
# Save to disk
|
|
195
|
+
try:
|
|
196
|
+
if self.config.format == HibernationFormat.JSON:
|
|
197
|
+
with open(file_path, 'w') as f:
|
|
198
|
+
json.dump(state, f, indent=2)
|
|
199
|
+
else: # PICKLE format — now uses JSON internally + HMAC signature
|
|
200
|
+
raw = json.dumps(state).encode('utf-8')
|
|
201
|
+
sig = hmac.new(self._hmac_key, raw, hashlib.sha256).hexdigest()
|
|
202
|
+
with open(file_path, 'wb') as f:
|
|
203
|
+
f.write(raw)
|
|
204
|
+
with open(file_path + ".sig", 'w', encoding='utf-8') as f:
|
|
205
|
+
f.write(sig)
|
|
206
|
+
|
|
207
|
+
# Get file size
|
|
208
|
+
state_size = os.path.getsize(file_path)
|
|
209
|
+
|
|
210
|
+
# Create metadata
|
|
211
|
+
metadata = HibernatedAgentMetadata(
|
|
212
|
+
agent_id=agent_id,
|
|
213
|
+
session_id=agent_context.session_id,
|
|
214
|
+
hibernated_at=datetime.now(),
|
|
215
|
+
state_file_path=file_path,
|
|
216
|
+
format=self.config.format,
|
|
217
|
+
state_size_bytes=state_size,
|
|
218
|
+
last_activity=self.agent_activity.get(agent_id, datetime.now()),
|
|
219
|
+
context_pointer=caas_pointer
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
# Track hibernated agent
|
|
223
|
+
self.hibernated_agents[agent_id] = metadata
|
|
224
|
+
self.agent_states[agent_id] = AgentState.HIBERNATED
|
|
225
|
+
|
|
226
|
+
self.logger.info(f"Agent {agent_id} hibernated successfully. State saved to {file_path} ({state_size} bytes)")
|
|
227
|
+
|
|
228
|
+
return metadata
|
|
229
|
+
|
|
230
|
+
except Exception as e:
|
|
231
|
+
self.logger.error(f"Failed to hibernate agent {agent_id}: {e}")
|
|
232
|
+
self.agent_states[agent_id] = AgentState.ACTIVE
|
|
233
|
+
raise
|
|
234
|
+
|
|
235
|
+
def wake_agent(self, agent_id: str) -> Dict[str, Any]:
|
|
236
|
+
"""
|
|
237
|
+
Wake up a hibernated agent by restoring its state from disk.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
agent_id: Unique identifier for the agent to wake
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
Dictionary containing the restored agent state
|
|
244
|
+
"""
|
|
245
|
+
if agent_id not in self.hibernated_agents:
|
|
246
|
+
raise ValueError(f"Agent {agent_id} is not hibernated")
|
|
247
|
+
|
|
248
|
+
self.logger.info(f"Waking agent {agent_id}")
|
|
249
|
+
|
|
250
|
+
# Update state
|
|
251
|
+
self.agent_states[agent_id] = AgentState.WAKING
|
|
252
|
+
|
|
253
|
+
metadata = self.hibernated_agents[agent_id]
|
|
254
|
+
|
|
255
|
+
try:
|
|
256
|
+
# Load state from disk
|
|
257
|
+
if metadata.format == HibernationFormat.JSON:
|
|
258
|
+
with open(metadata.state_file_path, 'r') as f:
|
|
259
|
+
state = json.load(f)
|
|
260
|
+
else: # PICKLE format — now uses JSON internally; verify HMAC before deserializing
|
|
261
|
+
sig_path = metadata.state_file_path + ".sig"
|
|
262
|
+
if not os.path.exists(sig_path):
|
|
263
|
+
raise ValueError(
|
|
264
|
+
f"Missing HMAC signature for {metadata.state_file_path} — "
|
|
265
|
+
"state file may have been tampered with"
|
|
266
|
+
)
|
|
267
|
+
with open(metadata.state_file_path, 'rb') as f:
|
|
268
|
+
raw = f.read()
|
|
269
|
+
with open(sig_path, 'r', encoding='utf-8') as f:
|
|
270
|
+
expected_sig = f.read().strip()
|
|
271
|
+
actual_sig = hmac.new(self._hmac_key, raw, hashlib.sha256).hexdigest()
|
|
272
|
+
if not hmac.compare_digest(actual_sig, expected_sig):
|
|
273
|
+
raise ValueError(
|
|
274
|
+
f"HMAC verification failed for {metadata.state_file_path} — "
|
|
275
|
+
"state file has been tampered with"
|
|
276
|
+
)
|
|
277
|
+
state = json.loads(raw.decode('utf-8'))
|
|
278
|
+
|
|
279
|
+
# Deserialize state
|
|
280
|
+
restored_state = self.deserialize_agent_state(state)
|
|
281
|
+
|
|
282
|
+
# Update tracking
|
|
283
|
+
self.agent_activity[agent_id] = datetime.now()
|
|
284
|
+
self.agent_states[agent_id] = AgentState.ACTIVE
|
|
285
|
+
|
|
286
|
+
# Remove from hibernated agents
|
|
287
|
+
del self.hibernated_agents[agent_id]
|
|
288
|
+
|
|
289
|
+
self.logger.info(f"Agent {agent_id} woken successfully")
|
|
290
|
+
|
|
291
|
+
return restored_state
|
|
292
|
+
|
|
293
|
+
except Exception as e:
|
|
294
|
+
self.logger.error(f"Failed to wake agent {agent_id}: {e}")
|
|
295
|
+
self.agent_states[agent_id] = AgentState.HIBERNATED
|
|
296
|
+
raise
|
|
297
|
+
|
|
298
|
+
def is_agent_hibernated(self, agent_id: str) -> bool:
|
|
299
|
+
"""Check if an agent is currently hibernated"""
|
|
300
|
+
return agent_id in self.hibernated_agents
|
|
301
|
+
|
|
302
|
+
def get_hibernated_agents(self) -> List[HibernatedAgentMetadata]:
|
|
303
|
+
"""Get list of all hibernated agents"""
|
|
304
|
+
return list(self.hibernated_agents.values())
|
|
305
|
+
|
|
306
|
+
def record_agent_activity(self, agent_id: str):
|
|
307
|
+
"""Record activity for an agent (resets idle timer)"""
|
|
308
|
+
self.agent_activity[agent_id] = datetime.now()
|
|
309
|
+
if agent_id not in self.agent_states or self.agent_states[agent_id] == AgentState.HIBERNATED:
|
|
310
|
+
self.agent_states[agent_id] = AgentState.ACTIVE
|
|
311
|
+
|
|
312
|
+
def get_idle_agents(self, min_idle_seconds: Optional[int] = None) -> List[str]:
|
|
313
|
+
"""
|
|
314
|
+
Get list of agent IDs that have been idle for the specified duration.
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
min_idle_seconds: Minimum idle time in seconds (uses config default if None)
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
List of agent IDs that are idle
|
|
321
|
+
"""
|
|
322
|
+
idle_threshold = min_idle_seconds or self.config.idle_timeout_seconds
|
|
323
|
+
now = datetime.now()
|
|
324
|
+
idle_agents = []
|
|
325
|
+
|
|
326
|
+
for agent_id, last_activity in self.agent_activity.items():
|
|
327
|
+
# Skip already hibernated agents
|
|
328
|
+
if self.is_agent_hibernated(agent_id):
|
|
329
|
+
continue
|
|
330
|
+
|
|
331
|
+
idle_time = (now - last_activity).total_seconds()
|
|
332
|
+
if idle_time >= idle_threshold:
|
|
333
|
+
idle_agents.append(agent_id)
|
|
334
|
+
|
|
335
|
+
return idle_agents
|
|
336
|
+
|
|
337
|
+
def cleanup_old_hibernated_agents(self, max_age_days: Optional[int] = None):
|
|
338
|
+
"""
|
|
339
|
+
Clean up hibernated agents older than the specified age.
|
|
340
|
+
|
|
341
|
+
Args:
|
|
342
|
+
max_age_days: Maximum age in days (uses config default if None)
|
|
343
|
+
"""
|
|
344
|
+
max_age = max_age_days or self.config.auto_cleanup_days
|
|
345
|
+
now = datetime.now()
|
|
346
|
+
cleanup_threshold = timedelta(days=max_age)
|
|
347
|
+
|
|
348
|
+
agents_to_cleanup = []
|
|
349
|
+
for agent_id, metadata in self.hibernated_agents.items():
|
|
350
|
+
age = now - metadata.hibernated_at
|
|
351
|
+
if age > cleanup_threshold:
|
|
352
|
+
agents_to_cleanup.append(agent_id)
|
|
353
|
+
|
|
354
|
+
for agent_id in agents_to_cleanup:
|
|
355
|
+
self._cleanup_hibernated_agent(agent_id)
|
|
356
|
+
|
|
357
|
+
def _cleanup_hibernated_agent(self, agent_id: str):
|
|
358
|
+
"""Remove hibernated agent and delete its state file"""
|
|
359
|
+
if agent_id not in self.hibernated_agents:
|
|
360
|
+
return
|
|
361
|
+
|
|
362
|
+
metadata = self.hibernated_agents[agent_id]
|
|
363
|
+
|
|
364
|
+
try:
|
|
365
|
+
# Delete state file
|
|
366
|
+
if os.path.exists(metadata.state_file_path):
|
|
367
|
+
os.remove(metadata.state_file_path)
|
|
368
|
+
|
|
369
|
+
# Remove from tracking
|
|
370
|
+
del self.hibernated_agents[agent_id]
|
|
371
|
+
if agent_id in self.agent_states:
|
|
372
|
+
del self.agent_states[agent_id]
|
|
373
|
+
if agent_id in self.agent_activity:
|
|
374
|
+
del self.agent_activity[agent_id]
|
|
375
|
+
|
|
376
|
+
self.logger.info(f"Cleaned up hibernated agent {agent_id}")
|
|
377
|
+
|
|
378
|
+
except Exception as e:
|
|
379
|
+
self.logger.error(f"Failed to cleanup hibernated agent {agent_id}: {e}")
|
|
380
|
+
|
|
381
|
+
def get_statistics(self) -> Dict[str, Any]:
|
|
382
|
+
"""Get statistics about hibernation"""
|
|
383
|
+
total_hibernated = len(self.hibernated_agents)
|
|
384
|
+
total_size = sum(m.state_size_bytes for m in self.hibernated_agents.values())
|
|
385
|
+
|
|
386
|
+
return {
|
|
387
|
+
"total_hibernated_agents": total_hibernated,
|
|
388
|
+
"total_state_size_bytes": total_size,
|
|
389
|
+
"total_state_size_mb": total_size / (1024 * 1024),
|
|
390
|
+
"active_agents": len([s for s in self.agent_states.values() if s == AgentState.ACTIVE]),
|
|
391
|
+
"storage_path": self.config.storage_path,
|
|
392
|
+
"format": self.config.format.value,
|
|
393
|
+
"idle_timeout_seconds": self.config.idle_timeout_seconds
|
|
394
|
+
}
|