claude-mpm 4.1.4__py3-none-any.whl → 4.1.6__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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/templates/research.json +39 -13
- claude_mpm/cli/__init__.py +2 -0
- claude_mpm/cli/commands/__init__.py +2 -0
- claude_mpm/cli/commands/configure.py +1221 -0
- claude_mpm/cli/commands/configure_tui.py +1921 -0
- claude_mpm/cli/commands/tickets.py +365 -784
- claude_mpm/cli/parsers/base_parser.py +7 -0
- claude_mpm/cli/parsers/configure_parser.py +119 -0
- claude_mpm/cli/startup_logging.py +39 -12
- claude_mpm/constants.py +1 -0
- claude_mpm/core/output_style_manager.py +24 -0
- claude_mpm/core/socketio_pool.py +35 -3
- claude_mpm/core/unified_agent_registry.py +46 -15
- claude_mpm/dashboard/static/css/connection-status.css +370 -0
- claude_mpm/dashboard/static/js/components/connection-debug.js +654 -0
- claude_mpm/dashboard/static/js/connection-manager.js +536 -0
- claude_mpm/dashboard/templates/index.html +11 -0
- claude_mpm/hooks/claude_hooks/services/__init__.py +3 -1
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +190 -0
- claude_mpm/services/agents/deployment/agent_discovery_service.py +12 -3
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +172 -233
- claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +575 -0
- claude_mpm/services/agents/deployment/agent_operation_service.py +573 -0
- claude_mpm/services/agents/deployment/agent_record_service.py +419 -0
- claude_mpm/services/agents/deployment/agent_state_service.py +381 -0
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +4 -2
- claude_mpm/services/diagnostics/checks/__init__.py +2 -0
- claude_mpm/services/diagnostics/checks/instructions_check.py +418 -0
- claude_mpm/services/diagnostics/diagnostic_runner.py +15 -2
- claude_mpm/services/event_bus/direct_relay.py +173 -0
- claude_mpm/services/infrastructure/__init__.py +31 -5
- claude_mpm/services/infrastructure/monitoring/__init__.py +43 -0
- claude_mpm/services/infrastructure/monitoring/aggregator.py +437 -0
- claude_mpm/services/infrastructure/monitoring/base.py +130 -0
- claude_mpm/services/infrastructure/monitoring/legacy.py +203 -0
- claude_mpm/services/infrastructure/monitoring/network.py +218 -0
- claude_mpm/services/infrastructure/monitoring/process.py +342 -0
- claude_mpm/services/infrastructure/monitoring/resources.py +243 -0
- claude_mpm/services/infrastructure/monitoring/service.py +367 -0
- claude_mpm/services/infrastructure/monitoring.py +67 -1030
- claude_mpm/services/project/analyzer.py +13 -4
- claude_mpm/services/project/analyzer_refactored.py +450 -0
- claude_mpm/services/project/analyzer_v2.py +566 -0
- claude_mpm/services/project/architecture_analyzer.py +461 -0
- claude_mpm/services/project/dependency_analyzer.py +462 -0
- claude_mpm/services/project/language_analyzer.py +265 -0
- claude_mpm/services/project/metrics_collector.py +410 -0
- claude_mpm/services/socketio/handlers/connection_handler.py +345 -0
- claude_mpm/services/socketio/server/broadcaster.py +32 -1
- claude_mpm/services/socketio/server/connection_manager.py +516 -0
- claude_mpm/services/socketio/server/core.py +63 -0
- claude_mpm/services/socketio/server/eventbus_integration.py +20 -9
- claude_mpm/services/socketio/server/main.py +27 -1
- claude_mpm/services/ticket_manager.py +5 -1
- claude_mpm/services/ticket_services/__init__.py +26 -0
- claude_mpm/services/ticket_services/crud_service.py +328 -0
- claude_mpm/services/ticket_services/formatter_service.py +290 -0
- claude_mpm/services/ticket_services/search_service.py +324 -0
- claude_mpm/services/ticket_services/validation_service.py +303 -0
- claude_mpm/services/ticket_services/workflow_service.py +244 -0
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.6.dist-info}/METADATA +3 -1
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.6.dist-info}/RECORD +67 -46
- claude_mpm/agents/OUTPUT_STYLE.md +0 -73
- claude_mpm/agents/backups/INSTRUCTIONS.md +0 -352
- claude_mpm/agents/templates/OPTIMIZATION_REPORT.md +0 -156
- claude_mpm/agents/templates/backup/data_engineer_agent_20250726_234551.json +0 -79
- claude_mpm/agents/templates/backup/documentation_agent_20250726_234551.json +0 -68
- claude_mpm/agents/templates/backup/engineer_agent_20250726_234551.json +0 -77
- claude_mpm/agents/templates/backup/ops_agent_20250726_234551.json +0 -78
- claude_mpm/agents/templates/backup/qa_agent_20250726_234551.json +0 -67
- claude_mpm/agents/templates/backup/research_agent_2025011_234551.json +0 -88
- claude_mpm/agents/templates/backup/research_agent_20250726_234551.json +0 -72
- claude_mpm/agents/templates/backup/research_memory_efficient.json +0 -88
- claude_mpm/agents/templates/backup/security_agent_20250726_234551.json +0 -78
- claude_mpm/agents/templates/backup/version_control_agent_20250726_234551.json +0 -62
- claude_mpm/agents/templates/vercel_ops_instructions.md +0 -582
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.6.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.6.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.6.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Agent Lifecycle Manager - Refactored Version
|
|
4
|
+
============================================
|
|
5
|
+
|
|
6
|
+
This is the refactored version of AgentLifecycleManager that delegates
|
|
7
|
+
responsibilities to specialized services following SOLID principles.
|
|
8
|
+
|
|
9
|
+
Line count reduction: From 1,020 lines to ~600 lines (40% reduction)
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import asyncio
|
|
13
|
+
from typing import Any, Dict, List, Optional
|
|
14
|
+
|
|
15
|
+
from claude_mpm.core.base_service import BaseService
|
|
16
|
+
from claude_mpm.services.agents.management import AgentManager
|
|
17
|
+
from claude_mpm.services.agents.memory import (
|
|
18
|
+
AgentPersistenceService,
|
|
19
|
+
PersistenceStrategy,
|
|
20
|
+
)
|
|
21
|
+
from claude_mpm.services.agents.registry import AgentRegistry
|
|
22
|
+
from claude_mpm.services.agents.registry.modification_tracker import (
|
|
23
|
+
AgentModification,
|
|
24
|
+
AgentModificationTracker,
|
|
25
|
+
ModificationTier,
|
|
26
|
+
ModificationType,
|
|
27
|
+
)
|
|
28
|
+
from claude_mpm.services.memory.cache.shared_prompt_cache import SharedPromptCache
|
|
29
|
+
|
|
30
|
+
# Import extracted services
|
|
31
|
+
from .agent_operation_service import (
|
|
32
|
+
AgentOperationService,
|
|
33
|
+
LifecycleOperation,
|
|
34
|
+
LifecycleOperationResult,
|
|
35
|
+
)
|
|
36
|
+
from .agent_record_service import AgentRecordService
|
|
37
|
+
from .agent_state_service import (
|
|
38
|
+
AgentLifecycleRecord,
|
|
39
|
+
AgentStateService,
|
|
40
|
+
LifecycleState,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Re-export for backward compatibility
|
|
44
|
+
__all__ = [
|
|
45
|
+
"AgentLifecycleManager",
|
|
46
|
+
"AgentLifecycleRecord",
|
|
47
|
+
"LifecycleOperation",
|
|
48
|
+
"LifecycleOperationResult",
|
|
49
|
+
"LifecycleState",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class AgentLifecycleManager(BaseService):
|
|
54
|
+
"""
|
|
55
|
+
Agent Lifecycle Manager - Orchestrates agent lifecycle through specialized services.
|
|
56
|
+
|
|
57
|
+
Refactored Architecture:
|
|
58
|
+
- AgentStateService: Manages agent states and transitions
|
|
59
|
+
- AgentOperationService: Handles CRUD operations
|
|
60
|
+
- AgentRecordService: Manages persistence and history
|
|
61
|
+
- LifecycleHealthChecker: Monitors system health
|
|
62
|
+
|
|
63
|
+
This manager now acts as a facade/orchestrator, delegating specific
|
|
64
|
+
responsibilities to appropriate services while maintaining backward
|
|
65
|
+
compatibility with existing interfaces.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
69
|
+
"""Initialize the lifecycle manager with configuration."""
|
|
70
|
+
super().__init__("agent_lifecycle_manager", config)
|
|
71
|
+
|
|
72
|
+
# Configuration
|
|
73
|
+
self.enable_auto_backup = self.get_config("enable_auto_backup", True)
|
|
74
|
+
self.enable_auto_validation = self.get_config("enable_auto_validation", True)
|
|
75
|
+
self.enable_cache_invalidation = self.get_config(
|
|
76
|
+
"enable_cache_invalidation", True
|
|
77
|
+
)
|
|
78
|
+
self.enable_registry_sync = self.get_config("enable_registry_sync", True)
|
|
79
|
+
self.default_persistence_strategy = PersistenceStrategy(
|
|
80
|
+
self.get_config(
|
|
81
|
+
"default_persistence_strategy", PersistenceStrategy.USER_OVERRIDE.value
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Core external services
|
|
86
|
+
self.shared_cache: Optional[SharedPromptCache] = None
|
|
87
|
+
self.agent_registry: Optional[AgentRegistry] = None
|
|
88
|
+
self.modification_tracker: Optional[AgentModificationTracker] = None
|
|
89
|
+
self.persistence_service: Optional[AgentPersistenceService] = None
|
|
90
|
+
self.agent_manager: Optional[AgentManager] = None
|
|
91
|
+
|
|
92
|
+
# Extracted internal services (composition over inheritance)
|
|
93
|
+
self.state_service = AgentStateService()
|
|
94
|
+
self.operation_service = AgentOperationService()
|
|
95
|
+
self.record_service = AgentRecordService()
|
|
96
|
+
|
|
97
|
+
# Performance metrics (maintained for compatibility)
|
|
98
|
+
self.performance_metrics = {
|
|
99
|
+
"total_operations": 0,
|
|
100
|
+
"successful_operations": 0,
|
|
101
|
+
"failed_operations": 0,
|
|
102
|
+
"average_duration_ms": 0.0,
|
|
103
|
+
"cache_hit_rate": 0.0,
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
self.logger.info("AgentLifecycleManager initialized (refactored)")
|
|
107
|
+
|
|
108
|
+
# Backward compatibility properties
|
|
109
|
+
@property
|
|
110
|
+
def agent_records(self) -> Dict[str, AgentLifecycleRecord]:
|
|
111
|
+
"""Get agent records from state service."""
|
|
112
|
+
return self.state_service.agent_records
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def operation_history(self) -> List[LifecycleOperationResult]:
|
|
116
|
+
"""Get operation history from operation service."""
|
|
117
|
+
return self.operation_service.operation_history
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def active_operations(self) -> Dict[str, LifecycleOperation]:
|
|
121
|
+
"""Get active operations from operation service."""
|
|
122
|
+
return self.operation_service.active_operations
|
|
123
|
+
|
|
124
|
+
async def _initialize(self) -> None:
|
|
125
|
+
"""Initialize the lifecycle manager and its services."""
|
|
126
|
+
self.logger.info("Initializing AgentLifecycleManager...")
|
|
127
|
+
|
|
128
|
+
# Initialize core external services
|
|
129
|
+
await self._initialize_core_services()
|
|
130
|
+
|
|
131
|
+
# Initialize extracted services
|
|
132
|
+
await self.state_service.start()
|
|
133
|
+
await self.operation_service.start()
|
|
134
|
+
await self.record_service.start()
|
|
135
|
+
|
|
136
|
+
# Wire up service dependencies
|
|
137
|
+
self.operation_service.agent_manager = self.agent_manager
|
|
138
|
+
self.operation_service.set_modification_tracker(self.modification_tracker)
|
|
139
|
+
|
|
140
|
+
# Load persisted data
|
|
141
|
+
records = await self.record_service.load_records()
|
|
142
|
+
self.state_service.agent_records = records
|
|
143
|
+
|
|
144
|
+
history = await self.record_service.load_history()
|
|
145
|
+
# Convert history back to operation results if needed
|
|
146
|
+
# For now, we'll start with empty history
|
|
147
|
+
|
|
148
|
+
# Setup service integrations
|
|
149
|
+
await self._setup_service_integrations()
|
|
150
|
+
|
|
151
|
+
# Perform initial registry sync
|
|
152
|
+
if self.enable_registry_sync:
|
|
153
|
+
await self._sync_with_registry()
|
|
154
|
+
|
|
155
|
+
self.logger.info("AgentLifecycleManager initialized successfully")
|
|
156
|
+
|
|
157
|
+
async def _cleanup(self) -> None:
|
|
158
|
+
"""Cleanup lifecycle manager and its services."""
|
|
159
|
+
self.logger.info("Cleaning up AgentLifecycleManager...")
|
|
160
|
+
|
|
161
|
+
# Save current state
|
|
162
|
+
await self.record_service.save_records(self.state_service.agent_records)
|
|
163
|
+
await self.record_service.save_history(self.operation_service.operation_history)
|
|
164
|
+
|
|
165
|
+
# Stop extracted services
|
|
166
|
+
await self.state_service.stop()
|
|
167
|
+
await self.operation_service.stop()
|
|
168
|
+
await self.record_service.stop()
|
|
169
|
+
|
|
170
|
+
# Stop core services if we own them
|
|
171
|
+
await self._cleanup_core_services()
|
|
172
|
+
|
|
173
|
+
self.logger.info("AgentLifecycleManager cleaned up")
|
|
174
|
+
|
|
175
|
+
async def _health_check(self) -> Dict[str, bool]:
|
|
176
|
+
"""Perform health checks on all services."""
|
|
177
|
+
from .lifecycle_health_checker import LifecycleHealthChecker
|
|
178
|
+
|
|
179
|
+
checker = LifecycleHealthChecker(self)
|
|
180
|
+
return await checker.perform_health_check()
|
|
181
|
+
|
|
182
|
+
async def _initialize_core_services(self) -> None:
|
|
183
|
+
"""Initialize core external service dependencies."""
|
|
184
|
+
try:
|
|
185
|
+
# Initialize SharedPromptCache
|
|
186
|
+
self.shared_cache = SharedPromptCache.get_instance()
|
|
187
|
+
|
|
188
|
+
# Initialize AgentRegistry
|
|
189
|
+
self.agent_registry = AgentRegistry(cache_service=self.shared_cache)
|
|
190
|
+
|
|
191
|
+
# Initialize AgentModificationTracker
|
|
192
|
+
self.modification_tracker = AgentModificationTracker()
|
|
193
|
+
await self.modification_tracker.start()
|
|
194
|
+
|
|
195
|
+
# Initialize AgentPersistenceService
|
|
196
|
+
self.persistence_service = AgentPersistenceService()
|
|
197
|
+
await self.persistence_service.start()
|
|
198
|
+
|
|
199
|
+
# Initialize AgentManager
|
|
200
|
+
self.agent_manager = AgentManager()
|
|
201
|
+
|
|
202
|
+
self.logger.info("Core services initialized successfully")
|
|
203
|
+
|
|
204
|
+
except Exception as e:
|
|
205
|
+
self.logger.error(f"Failed to initialize core services: {e}")
|
|
206
|
+
raise
|
|
207
|
+
|
|
208
|
+
async def _setup_service_integrations(self) -> None:
|
|
209
|
+
"""Set up integrations between services."""
|
|
210
|
+
try:
|
|
211
|
+
# Register modification callback
|
|
212
|
+
if self.modification_tracker:
|
|
213
|
+
self.modification_tracker.register_modification_callback(
|
|
214
|
+
self._handle_modification_event
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
self.logger.debug("Service integrations set up successfully")
|
|
218
|
+
|
|
219
|
+
except Exception as e:
|
|
220
|
+
self.logger.warning(f"Failed to setup some service integrations: {e}")
|
|
221
|
+
|
|
222
|
+
async def _sync_with_registry(self) -> None:
|
|
223
|
+
"""Synchronize state with agent registry."""
|
|
224
|
+
try:
|
|
225
|
+
if not self.agent_registry:
|
|
226
|
+
return
|
|
227
|
+
|
|
228
|
+
# Discover all agents
|
|
229
|
+
self.agent_registry.discover_agents()
|
|
230
|
+
all_agents = self.agent_registry.list_agents()
|
|
231
|
+
|
|
232
|
+
# Update state service with registry data
|
|
233
|
+
for agent_metadata in all_agents:
|
|
234
|
+
if not self.state_service.get_record(agent_metadata.name):
|
|
235
|
+
# Map tier strings to enums
|
|
236
|
+
tier_map = {
|
|
237
|
+
"project": ModificationTier.PROJECT,
|
|
238
|
+
"user": ModificationTier.USER,
|
|
239
|
+
"system": ModificationTier.SYSTEM,
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
# Create new record
|
|
243
|
+
self.state_service.create_record(
|
|
244
|
+
agent_name=agent_metadata.name,
|
|
245
|
+
tier=tier_map.get(agent_metadata.tier, ModificationTier.USER),
|
|
246
|
+
file_path=agent_metadata.path,
|
|
247
|
+
initial_state=LifecycleState.ACTIVE,
|
|
248
|
+
version="1.0.0",
|
|
249
|
+
type=agent_metadata.type,
|
|
250
|
+
description=agent_metadata.description,
|
|
251
|
+
capabilities=agent_metadata.capabilities,
|
|
252
|
+
validated=agent_metadata.validated,
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
self.logger.info(f"Synchronized with registry: {len(all_agents)} agents")
|
|
256
|
+
|
|
257
|
+
except Exception as e:
|
|
258
|
+
self.logger.error(f"Failed to sync with registry: {e}")
|
|
259
|
+
|
|
260
|
+
# Main lifecycle operations (orchestration layer)
|
|
261
|
+
|
|
262
|
+
async def create_agent(
|
|
263
|
+
self,
|
|
264
|
+
agent_name: str,
|
|
265
|
+
agent_content: str,
|
|
266
|
+
tier: ModificationTier = ModificationTier.USER,
|
|
267
|
+
agent_type: str = "custom",
|
|
268
|
+
**kwargs,
|
|
269
|
+
) -> LifecycleOperationResult:
|
|
270
|
+
"""
|
|
271
|
+
Create a new agent through orchestrated services.
|
|
272
|
+
|
|
273
|
+
Orchestration flow:
|
|
274
|
+
1. Check state service for existing agent
|
|
275
|
+
2. Execute creation through operation service
|
|
276
|
+
3. Update state service with new record
|
|
277
|
+
4. Invalidate cache and update registry
|
|
278
|
+
5. Track metrics
|
|
279
|
+
"""
|
|
280
|
+
# Check if agent already exists
|
|
281
|
+
if self.state_service.get_record(agent_name):
|
|
282
|
+
return LifecycleOperationResult(
|
|
283
|
+
operation=LifecycleOperation.CREATE,
|
|
284
|
+
agent_name=agent_name,
|
|
285
|
+
success=False,
|
|
286
|
+
duration_ms=0,
|
|
287
|
+
error_message="Agent already exists",
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
# Execute creation
|
|
291
|
+
result = await self.operation_service.create_agent(
|
|
292
|
+
agent_name=agent_name,
|
|
293
|
+
agent_content=agent_content,
|
|
294
|
+
tier=tier,
|
|
295
|
+
agent_type=agent_type,
|
|
296
|
+
**kwargs,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
# Update state if successful
|
|
300
|
+
if result.success:
|
|
301
|
+
# Create state record
|
|
302
|
+
record = self.state_service.create_record(
|
|
303
|
+
agent_name=agent_name,
|
|
304
|
+
tier=tier,
|
|
305
|
+
file_path=result.metadata.get("file_path", ""),
|
|
306
|
+
initial_state=LifecycleState.ACTIVE,
|
|
307
|
+
version="1.0.0",
|
|
308
|
+
agent_type=agent_type,
|
|
309
|
+
**kwargs,
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
# Track modification
|
|
313
|
+
if result.modification_id:
|
|
314
|
+
self.state_service.add_modification(agent_name, result.modification_id)
|
|
315
|
+
|
|
316
|
+
# Handle cache and registry
|
|
317
|
+
result.cache_invalidated = await self._invalidate_agent_cache(agent_name)
|
|
318
|
+
result.registry_updated = await self._update_registry(agent_name)
|
|
319
|
+
|
|
320
|
+
# Update metrics
|
|
321
|
+
await self._update_performance_metrics(result)
|
|
322
|
+
|
|
323
|
+
return result
|
|
324
|
+
|
|
325
|
+
async def update_agent(
|
|
326
|
+
self, agent_name: str, agent_content: str, **kwargs
|
|
327
|
+
) -> LifecycleOperationResult:
|
|
328
|
+
"""
|
|
329
|
+
Update an existing agent through orchestrated services.
|
|
330
|
+
|
|
331
|
+
Orchestration flow:
|
|
332
|
+
1. Verify agent exists in state service
|
|
333
|
+
2. Execute update through operation service
|
|
334
|
+
3. Update state and version
|
|
335
|
+
4. Invalidate cache and update registry
|
|
336
|
+
5. Track metrics
|
|
337
|
+
"""
|
|
338
|
+
# Check if agent exists
|
|
339
|
+
record = self.state_service.get_record(agent_name)
|
|
340
|
+
if not record:
|
|
341
|
+
return LifecycleOperationResult(
|
|
342
|
+
operation=LifecycleOperation.UPDATE,
|
|
343
|
+
agent_name=agent_name,
|
|
344
|
+
success=False,
|
|
345
|
+
duration_ms=0,
|
|
346
|
+
error_message="Agent not found",
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
# Execute update
|
|
350
|
+
result = await self.operation_service.update_agent(
|
|
351
|
+
agent_name=agent_name,
|
|
352
|
+
agent_content=agent_content,
|
|
353
|
+
file_path=record.file_path,
|
|
354
|
+
tier=record.tier,
|
|
355
|
+
**kwargs,
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
# Update state if successful
|
|
359
|
+
if result.success:
|
|
360
|
+
# Update state
|
|
361
|
+
self.state_service.update_state(
|
|
362
|
+
agent_name, LifecycleState.MODIFIED, "Agent updated"
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
# Increment version
|
|
366
|
+
new_version = self.state_service.increment_version(agent_name)
|
|
367
|
+
result.metadata["new_version"] = new_version
|
|
368
|
+
|
|
369
|
+
# Track modification
|
|
370
|
+
if result.modification_id:
|
|
371
|
+
self.state_service.add_modification(agent_name, result.modification_id)
|
|
372
|
+
|
|
373
|
+
# Handle cache and registry
|
|
374
|
+
result.cache_invalidated = await self._invalidate_agent_cache(agent_name)
|
|
375
|
+
result.registry_updated = await self._update_registry(agent_name)
|
|
376
|
+
|
|
377
|
+
# Update metrics
|
|
378
|
+
await self._update_performance_metrics(result)
|
|
379
|
+
|
|
380
|
+
return result
|
|
381
|
+
|
|
382
|
+
async def delete_agent(self, agent_name: str, **kwargs) -> LifecycleOperationResult:
|
|
383
|
+
"""
|
|
384
|
+
Delete an agent through orchestrated services.
|
|
385
|
+
|
|
386
|
+
Orchestration flow:
|
|
387
|
+
1. Verify agent exists in state service
|
|
388
|
+
2. Create backup if enabled
|
|
389
|
+
3. Execute deletion through operation service
|
|
390
|
+
4. Update state to DELETED
|
|
391
|
+
5. Invalidate cache and update registry
|
|
392
|
+
6. Track metrics
|
|
393
|
+
"""
|
|
394
|
+
# Check if agent exists
|
|
395
|
+
record = self.state_service.get_record(agent_name)
|
|
396
|
+
if not record:
|
|
397
|
+
return LifecycleOperationResult(
|
|
398
|
+
operation=LifecycleOperation.DELETE,
|
|
399
|
+
agent_name=agent_name,
|
|
400
|
+
success=False,
|
|
401
|
+
duration_ms=0,
|
|
402
|
+
error_message="Agent not found",
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
# Execute deletion
|
|
406
|
+
result = await self.operation_service.delete_agent(
|
|
407
|
+
agent_name=agent_name,
|
|
408
|
+
file_path=record.file_path,
|
|
409
|
+
tier=record.tier,
|
|
410
|
+
create_backup=self.enable_auto_backup,
|
|
411
|
+
**kwargs,
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
# Update state if successful
|
|
415
|
+
if result.success:
|
|
416
|
+
# Update state
|
|
417
|
+
self.state_service.update_state(
|
|
418
|
+
agent_name, LifecycleState.DELETED, "Agent deleted"
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
# Track backup path
|
|
422
|
+
if result.metadata.get("backup_path"):
|
|
423
|
+
self.state_service.add_backup_path(
|
|
424
|
+
agent_name, result.metadata["backup_path"]
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
# Track modification
|
|
428
|
+
if result.modification_id:
|
|
429
|
+
self.state_service.add_modification(agent_name, result.modification_id)
|
|
430
|
+
|
|
431
|
+
# Handle cache and registry
|
|
432
|
+
result.cache_invalidated = await self._invalidate_agent_cache(agent_name)
|
|
433
|
+
result.registry_updated = await self._update_registry(agent_name)
|
|
434
|
+
|
|
435
|
+
# Update metrics
|
|
436
|
+
await self._update_performance_metrics(result)
|
|
437
|
+
|
|
438
|
+
return result
|
|
439
|
+
|
|
440
|
+
async def restore_agent(
|
|
441
|
+
self, agent_name: str, backup_path: Optional[str] = None
|
|
442
|
+
) -> LifecycleOperationResult:
|
|
443
|
+
"""Restore an agent from backup."""
|
|
444
|
+
from .agent_restore_handler import AgentRestoreHandler
|
|
445
|
+
|
|
446
|
+
handler = AgentRestoreHandler(self)
|
|
447
|
+
return await handler.restore_agent(agent_name, backup_path)
|
|
448
|
+
|
|
449
|
+
# Query methods (delegated to services)
|
|
450
|
+
|
|
451
|
+
async def get_agent_status(self, agent_name: str) -> Optional[AgentLifecycleRecord]:
|
|
452
|
+
"""Get current status of an agent."""
|
|
453
|
+
return self.state_service.get_record(agent_name)
|
|
454
|
+
|
|
455
|
+
async def list_agents(
|
|
456
|
+
self, state_filter: Optional[LifecycleState] = None
|
|
457
|
+
) -> List[AgentLifecycleRecord]:
|
|
458
|
+
"""List agents with optional state filtering."""
|
|
459
|
+
return self.state_service.list_agents_by_state(state_filter)
|
|
460
|
+
|
|
461
|
+
async def get_operation_history(
|
|
462
|
+
self, agent_name: Optional[str] = None, limit: int = 100
|
|
463
|
+
) -> List[LifecycleOperationResult]:
|
|
464
|
+
"""Get operation history with optional filtering."""
|
|
465
|
+
return self.operation_service.get_operation_history(agent_name, limit)
|
|
466
|
+
|
|
467
|
+
async def get_lifecycle_stats(self) -> Dict[str, Any]:
|
|
468
|
+
"""Get comprehensive lifecycle statistics."""
|
|
469
|
+
# Combine statistics from all services
|
|
470
|
+
stats = {
|
|
471
|
+
"total_agents": len(self.state_service.agent_records),
|
|
472
|
+
"active_operations": len(self.operation_service.active_operations),
|
|
473
|
+
"performance_metrics": self.operation_service.get_metrics(),
|
|
474
|
+
"agents_by_state": self.state_service.get_state_statistics(),
|
|
475
|
+
"agents_by_tier": self.state_service.get_tier_statistics(),
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
# Add record service statistics
|
|
479
|
+
record_stats = await self.record_service.get_statistics()
|
|
480
|
+
stats.update(record_stats)
|
|
481
|
+
|
|
482
|
+
return stats
|
|
483
|
+
|
|
484
|
+
# Support methods
|
|
485
|
+
|
|
486
|
+
async def _invalidate_agent_cache(self, agent_name: str) -> bool:
|
|
487
|
+
"""Invalidate cache entries for an agent."""
|
|
488
|
+
if not self.enable_cache_invalidation or not self.shared_cache:
|
|
489
|
+
return False
|
|
490
|
+
|
|
491
|
+
try:
|
|
492
|
+
patterns = [
|
|
493
|
+
f"agent_profile:{agent_name}:*",
|
|
494
|
+
f"task_prompt:{agent_name}:*",
|
|
495
|
+
"agent_registry_discovery",
|
|
496
|
+
f"agent_profile_enhanced:{agent_name}:*",
|
|
497
|
+
]
|
|
498
|
+
|
|
499
|
+
for pattern in patterns:
|
|
500
|
+
await asyncio.get_event_loop().run_in_executor(
|
|
501
|
+
None, lambda p=pattern: self.shared_cache.invalidate(p)
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
return True
|
|
505
|
+
|
|
506
|
+
except Exception as e:
|
|
507
|
+
self.logger.warning(f"Failed to invalidate cache for {agent_name}: {e}")
|
|
508
|
+
return False
|
|
509
|
+
|
|
510
|
+
async def _update_registry(self, agent_name: str) -> bool:
|
|
511
|
+
"""Update agent registry after modification."""
|
|
512
|
+
if not self.enable_registry_sync or not self.agent_registry:
|
|
513
|
+
return False
|
|
514
|
+
|
|
515
|
+
try:
|
|
516
|
+
# Refresh registry
|
|
517
|
+
self.agent_registry.discover_agents()
|
|
518
|
+
return True
|
|
519
|
+
|
|
520
|
+
except Exception as e:
|
|
521
|
+
self.logger.warning(f"Failed to update registry for {agent_name}: {e}")
|
|
522
|
+
return False
|
|
523
|
+
|
|
524
|
+
async def _update_performance_metrics(
|
|
525
|
+
self, result: LifecycleOperationResult
|
|
526
|
+
) -> None:
|
|
527
|
+
"""Update performance metrics with operation result."""
|
|
528
|
+
from .lifecycle_performance_tracker import LifecyclePerformanceTracker
|
|
529
|
+
|
|
530
|
+
tracker = LifecyclePerformanceTracker(self.performance_metrics)
|
|
531
|
+
tracker.update_metrics(result)
|
|
532
|
+
|
|
533
|
+
async def _handle_modification_event(self, modification: AgentModification) -> None:
|
|
534
|
+
"""Handle modification events from tracker."""
|
|
535
|
+
try:
|
|
536
|
+
agent_name = modification.agent_name
|
|
537
|
+
|
|
538
|
+
# Update state based on modification
|
|
539
|
+
if modification.modification_type == ModificationType.DELETE:
|
|
540
|
+
self.state_service.update_state(
|
|
541
|
+
agent_name, LifecycleState.DELETED, "External deletion detected"
|
|
542
|
+
)
|
|
543
|
+
elif modification.modification_type in [
|
|
544
|
+
ModificationType.CREATE,
|
|
545
|
+
ModificationType.MODIFY,
|
|
546
|
+
]:
|
|
547
|
+
self.state_service.update_state(
|
|
548
|
+
agent_name,
|
|
549
|
+
LifecycleState.MODIFIED,
|
|
550
|
+
"External modification detected",
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
# Track modification
|
|
554
|
+
self.state_service.add_modification(
|
|
555
|
+
agent_name, modification.modification_id
|
|
556
|
+
)
|
|
557
|
+
|
|
558
|
+
self.logger.debug(
|
|
559
|
+
f"Handled {modification.modification_type.value} event for {agent_name}"
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
except Exception as e:
|
|
563
|
+
self.logger.error(f"Error handling modification event: {e}")
|
|
564
|
+
|
|
565
|
+
async def _cleanup_core_services(self) -> None:
|
|
566
|
+
"""Cleanup core services if we manage their lifecycle."""
|
|
567
|
+
try:
|
|
568
|
+
if self.modification_tracker:
|
|
569
|
+
await self.modification_tracker.stop()
|
|
570
|
+
|
|
571
|
+
if self.persistence_service:
|
|
572
|
+
await self.persistence_service.stop()
|
|
573
|
+
|
|
574
|
+
except Exception as e:
|
|
575
|
+
self.logger.error(f"Error cleaning up core services: {e}")
|