claude-mpm 4.0.17__py3-none-any.whl → 4.0.20__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/__main__.py +4 -0
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +38 -2
- claude_mpm/agents/OUTPUT_STYLE.md +84 -0
- claude_mpm/agents/templates/qa.json +24 -12
- claude_mpm/cli/__init__.py +85 -1
- claude_mpm/cli/__main__.py +4 -0
- claude_mpm/cli/commands/mcp_install_commands.py +62 -5
- claude_mpm/cli/commands/mcp_server_commands.py +60 -79
- claude_mpm/cli/commands/memory.py +32 -5
- claude_mpm/cli/commands/run.py +33 -6
- claude_mpm/cli/parsers/base_parser.py +5 -0
- claude_mpm/cli/parsers/run_parser.py +5 -0
- claude_mpm/cli/utils.py +17 -4
- claude_mpm/core/base_service.py +1 -1
- claude_mpm/core/config.py +70 -5
- claude_mpm/core/framework_loader.py +342 -31
- claude_mpm/core/interactive_session.py +55 -1
- claude_mpm/core/oneshot_session.py +7 -1
- claude_mpm/core/output_style_manager.py +468 -0
- claude_mpm/core/unified_paths.py +190 -21
- claude_mpm/hooks/claude_hooks/hook_handler.py +91 -16
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +3 -0
- claude_mpm/init.py +1 -0
- claude_mpm/scripts/mcp_server.py +68 -0
- claude_mpm/scripts/mcp_wrapper.py +39 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +151 -7
- claude_mpm/services/agents/deployment/agent_template_builder.py +37 -1
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +441 -0
- claude_mpm/services/agents/memory/__init__.py +0 -2
- claude_mpm/services/agents/memory/agent_memory_manager.py +737 -43
- claude_mpm/services/agents/memory/content_manager.py +144 -14
- claude_mpm/services/agents/memory/template_generator.py +7 -354
- claude_mpm/services/mcp_gateway/core/singleton_manager.py +312 -0
- claude_mpm/services/mcp_gateway/core/startup_verification.py +315 -0
- claude_mpm/services/mcp_gateway/main.py +7 -0
- claude_mpm/services/mcp_gateway/server/stdio_server.py +184 -176
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +453 -0
- claude_mpm/services/subprocess_launcher_service.py +5 -0
- {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/METADATA +1 -1
- {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/RECORD +45 -38
- {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/entry_points.txt +1 -0
- claude_mpm/services/agents/memory/analyzer.py +0 -430
- {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/WHEEL +0 -0
- {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
"""Multi-Source Agent Deployment Service
|
|
2
|
+
|
|
3
|
+
This service implements proper version comparison across multiple agent sources,
|
|
4
|
+
ensuring the highest version agent is deployed regardless of source.
|
|
5
|
+
|
|
6
|
+
Key Features:
|
|
7
|
+
- Discovers agents from multiple sources (system templates, project agents, user agents)
|
|
8
|
+
- Compares versions across all sources
|
|
9
|
+
- Deploys the highest version for each agent
|
|
10
|
+
- Tracks which source provided the deployed agent
|
|
11
|
+
- Maintains backward compatibility with existing deployment modes
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import logging
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
18
|
+
|
|
19
|
+
from claude_mpm.core.config import Config
|
|
20
|
+
from claude_mpm.core.logging_config import get_logger
|
|
21
|
+
|
|
22
|
+
from .agent_discovery_service import AgentDiscoveryService
|
|
23
|
+
from .agent_version_manager import AgentVersionManager
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class MultiSourceAgentDeploymentService:
|
|
27
|
+
"""Service for deploying agents from multiple sources with version comparison.
|
|
28
|
+
|
|
29
|
+
This service ensures that the highest version of each agent is deployed,
|
|
30
|
+
regardless of whether it comes from system templates, project agents, or
|
|
31
|
+
user agents.
|
|
32
|
+
|
|
33
|
+
WHY: The current system processes agents from a single source at a time,
|
|
34
|
+
which can result in lower version agents being deployed if they exist in
|
|
35
|
+
a higher priority source. This service fixes that by comparing versions
|
|
36
|
+
across all sources.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(self):
|
|
40
|
+
"""Initialize the multi-source deployment service."""
|
|
41
|
+
self.logger = get_logger(__name__)
|
|
42
|
+
self.version_manager = AgentVersionManager()
|
|
43
|
+
|
|
44
|
+
def discover_agents_from_all_sources(
|
|
45
|
+
self,
|
|
46
|
+
system_templates_dir: Optional[Path] = None,
|
|
47
|
+
project_agents_dir: Optional[Path] = None,
|
|
48
|
+
user_agents_dir: Optional[Path] = None,
|
|
49
|
+
working_directory: Optional[Path] = None
|
|
50
|
+
) -> Dict[str, List[Dict[str, Any]]]:
|
|
51
|
+
"""Discover agents from all available sources.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
system_templates_dir: Directory containing system agent templates
|
|
55
|
+
project_agents_dir: Directory containing project-specific agents
|
|
56
|
+
user_agents_dir: Directory containing user custom agents
|
|
57
|
+
working_directory: Current working directory for finding project agents
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Dictionary mapping agent names to list of agent info from different sources
|
|
61
|
+
"""
|
|
62
|
+
agents_by_name = {}
|
|
63
|
+
|
|
64
|
+
# Determine directories if not provided
|
|
65
|
+
if not system_templates_dir:
|
|
66
|
+
# Use default system templates location
|
|
67
|
+
from claude_mpm.config.paths import paths
|
|
68
|
+
system_templates_dir = paths.agents_dir / "templates"
|
|
69
|
+
|
|
70
|
+
if not project_agents_dir and working_directory:
|
|
71
|
+
# Check for project agents in working directory
|
|
72
|
+
project_agents_dir = working_directory / ".claude-mpm" / "agents"
|
|
73
|
+
if not project_agents_dir.exists():
|
|
74
|
+
project_agents_dir = None
|
|
75
|
+
|
|
76
|
+
if not user_agents_dir:
|
|
77
|
+
# Check for user agents in home directory
|
|
78
|
+
user_agents_dir = Path.home() / ".claude-mpm" / "agents"
|
|
79
|
+
if not user_agents_dir.exists():
|
|
80
|
+
user_agents_dir = None
|
|
81
|
+
|
|
82
|
+
# Discover agents from each source
|
|
83
|
+
sources = [
|
|
84
|
+
("system", system_templates_dir),
|
|
85
|
+
("project", project_agents_dir),
|
|
86
|
+
("user", user_agents_dir)
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
for source_name, source_dir in sources:
|
|
90
|
+
if source_dir and source_dir.exists():
|
|
91
|
+
self.logger.debug(f"Discovering agents from {source_name} source: {source_dir}")
|
|
92
|
+
discovery_service = AgentDiscoveryService(source_dir)
|
|
93
|
+
agents = discovery_service.list_available_agents()
|
|
94
|
+
|
|
95
|
+
for agent_info in agents:
|
|
96
|
+
agent_name = agent_info.get("name")
|
|
97
|
+
if not agent_name:
|
|
98
|
+
continue
|
|
99
|
+
|
|
100
|
+
# Add source information
|
|
101
|
+
agent_info["source"] = source_name
|
|
102
|
+
agent_info["source_dir"] = str(source_dir)
|
|
103
|
+
|
|
104
|
+
# Initialize list if this is the first occurrence of this agent
|
|
105
|
+
if agent_name not in agents_by_name:
|
|
106
|
+
agents_by_name[agent_name] = []
|
|
107
|
+
|
|
108
|
+
agents_by_name[agent_name].append(agent_info)
|
|
109
|
+
|
|
110
|
+
self.logger.info(
|
|
111
|
+
f"Discovered {len(agents)} agents from {source_name} source"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
return agents_by_name
|
|
115
|
+
|
|
116
|
+
def select_highest_version_agents(
|
|
117
|
+
self,
|
|
118
|
+
agents_by_name: Dict[str, List[Dict[str, Any]]]
|
|
119
|
+
) -> Dict[str, Dict[str, Any]]:
|
|
120
|
+
"""Select the highest version agent from multiple sources.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
agents_by_name: Dictionary mapping agent names to list of agent info
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
Dictionary mapping agent names to the highest version agent info
|
|
127
|
+
"""
|
|
128
|
+
selected_agents = {}
|
|
129
|
+
|
|
130
|
+
for agent_name, agent_versions in agents_by_name.items():
|
|
131
|
+
if not agent_versions:
|
|
132
|
+
continue
|
|
133
|
+
|
|
134
|
+
# If only one version exists, use it
|
|
135
|
+
if len(agent_versions) == 1:
|
|
136
|
+
selected_agents[agent_name] = agent_versions[0]
|
|
137
|
+
self.logger.debug(
|
|
138
|
+
f"Agent '{agent_name}' has single source: {agent_versions[0]['source']}"
|
|
139
|
+
)
|
|
140
|
+
continue
|
|
141
|
+
|
|
142
|
+
# Compare versions to find the highest
|
|
143
|
+
highest_version_agent = None
|
|
144
|
+
highest_version_tuple = (0, 0, 0)
|
|
145
|
+
|
|
146
|
+
for agent_info in agent_versions:
|
|
147
|
+
version_str = agent_info.get("version", "0.0.0")
|
|
148
|
+
version_tuple = self.version_manager.parse_version(version_str)
|
|
149
|
+
|
|
150
|
+
self.logger.debug(
|
|
151
|
+
f"Agent '{agent_name}' from {agent_info['source']}: "
|
|
152
|
+
f"version {version_str} -> {version_tuple}"
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# Compare with current highest
|
|
156
|
+
if self.version_manager.compare_versions(version_tuple, highest_version_tuple) > 0:
|
|
157
|
+
highest_version_agent = agent_info
|
|
158
|
+
highest_version_tuple = version_tuple
|
|
159
|
+
|
|
160
|
+
if highest_version_agent:
|
|
161
|
+
selected_agents[agent_name] = highest_version_agent
|
|
162
|
+
self.logger.info(
|
|
163
|
+
f"Selected agent '{agent_name}' version {highest_version_agent['version']} "
|
|
164
|
+
f"from {highest_version_agent['source']} source"
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# Log if a higher priority source was overridden by version
|
|
168
|
+
for other_agent in agent_versions:
|
|
169
|
+
if other_agent != highest_version_agent:
|
|
170
|
+
other_version = self.version_manager.parse_version(
|
|
171
|
+
other_agent.get("version", "0.0.0")
|
|
172
|
+
)
|
|
173
|
+
if other_agent["source"] == "project" and highest_version_agent["source"] == "system":
|
|
174
|
+
self.logger.warning(
|
|
175
|
+
f"Project agent '{agent_name}' v{other_agent['version']} "
|
|
176
|
+
f"overridden by higher system version v{highest_version_agent['version']}"
|
|
177
|
+
)
|
|
178
|
+
elif other_agent["source"] == "user" and highest_version_agent["source"] in ["system", "project"]:
|
|
179
|
+
self.logger.warning(
|
|
180
|
+
f"User agent '{agent_name}' v{other_agent['version']} "
|
|
181
|
+
f"overridden by higher {highest_version_agent['source']} version v{highest_version_agent['version']}"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
return selected_agents
|
|
185
|
+
|
|
186
|
+
def get_agents_for_deployment(
|
|
187
|
+
self,
|
|
188
|
+
system_templates_dir: Optional[Path] = None,
|
|
189
|
+
project_agents_dir: Optional[Path] = None,
|
|
190
|
+
user_agents_dir: Optional[Path] = None,
|
|
191
|
+
working_directory: Optional[Path] = None,
|
|
192
|
+
excluded_agents: Optional[List[str]] = None,
|
|
193
|
+
config: Optional[Config] = None
|
|
194
|
+
) -> Tuple[Dict[str, Path], Dict[str, str]]:
|
|
195
|
+
"""Get the highest version agents from all sources for deployment.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
system_templates_dir: Directory containing system agent templates
|
|
199
|
+
project_agents_dir: Directory containing project-specific agents
|
|
200
|
+
user_agents_dir: Directory containing user custom agents
|
|
201
|
+
working_directory: Current working directory for finding project agents
|
|
202
|
+
excluded_agents: List of agent names to exclude from deployment
|
|
203
|
+
config: Configuration object for additional filtering
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
Tuple of:
|
|
207
|
+
- Dictionary mapping agent names to template file paths
|
|
208
|
+
- Dictionary mapping agent names to their source
|
|
209
|
+
"""
|
|
210
|
+
# Discover all available agents
|
|
211
|
+
agents_by_name = self.discover_agents_from_all_sources(
|
|
212
|
+
system_templates_dir=system_templates_dir,
|
|
213
|
+
project_agents_dir=project_agents_dir,
|
|
214
|
+
user_agents_dir=user_agents_dir,
|
|
215
|
+
working_directory=working_directory
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# Select highest version for each agent
|
|
219
|
+
selected_agents = self.select_highest_version_agents(agents_by_name)
|
|
220
|
+
|
|
221
|
+
# Apply exclusion filters
|
|
222
|
+
if excluded_agents:
|
|
223
|
+
for agent_name in excluded_agents:
|
|
224
|
+
if agent_name in selected_agents:
|
|
225
|
+
self.logger.info(f"Excluding agent '{agent_name}' from deployment")
|
|
226
|
+
del selected_agents[agent_name]
|
|
227
|
+
|
|
228
|
+
# Apply config-based filtering if provided
|
|
229
|
+
if config:
|
|
230
|
+
selected_agents = self._apply_config_filters(selected_agents, config)
|
|
231
|
+
|
|
232
|
+
# Create deployment mappings
|
|
233
|
+
agents_to_deploy = {}
|
|
234
|
+
agent_sources = {}
|
|
235
|
+
|
|
236
|
+
for agent_name, agent_info in selected_agents.items():
|
|
237
|
+
template_path = Path(agent_info["path"])
|
|
238
|
+
if template_path.exists():
|
|
239
|
+
# Use the file stem as the key for consistency
|
|
240
|
+
file_stem = template_path.stem
|
|
241
|
+
agents_to_deploy[file_stem] = template_path
|
|
242
|
+
agent_sources[file_stem] = agent_info["source"]
|
|
243
|
+
|
|
244
|
+
# Also keep the display name mapping for logging
|
|
245
|
+
if file_stem != agent_name:
|
|
246
|
+
self.logger.debug(f"Mapping '{agent_name}' -> '{file_stem}'")
|
|
247
|
+
else:
|
|
248
|
+
self.logger.warning(
|
|
249
|
+
f"Template file not found for agent '{agent_name}': {template_path}"
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
self.logger.info(
|
|
253
|
+
f"Selected {len(agents_to_deploy)} agents for deployment "
|
|
254
|
+
f"(system: {sum(1 for s in agent_sources.values() if s == 'system')}, "
|
|
255
|
+
f"project: {sum(1 for s in agent_sources.values() if s == 'project')}, "
|
|
256
|
+
f"user: {sum(1 for s in agent_sources.values() if s == 'user')})"
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
return agents_to_deploy, agent_sources
|
|
260
|
+
|
|
261
|
+
def _apply_config_filters(
|
|
262
|
+
self,
|
|
263
|
+
selected_agents: Dict[str, Dict[str, Any]],
|
|
264
|
+
config: Config
|
|
265
|
+
) -> Dict[str, Dict[str, Any]]:
|
|
266
|
+
"""Apply configuration-based filtering to selected agents.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
selected_agents: Dictionary of selected agents
|
|
270
|
+
config: Configuration object
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
Filtered dictionary of agents
|
|
274
|
+
"""
|
|
275
|
+
filtered_agents = {}
|
|
276
|
+
|
|
277
|
+
# Get exclusion patterns from config
|
|
278
|
+
exclusion_patterns = config.get("agent_deployment.exclusion_patterns", [])
|
|
279
|
+
|
|
280
|
+
# Get environment-specific exclusions
|
|
281
|
+
environment = config.get("environment", "development")
|
|
282
|
+
env_exclusions = config.get(f"agent_deployment.{environment}_exclusions", [])
|
|
283
|
+
|
|
284
|
+
for agent_name, agent_info in selected_agents.items():
|
|
285
|
+
# Check exclusion patterns
|
|
286
|
+
excluded = False
|
|
287
|
+
|
|
288
|
+
for pattern in exclusion_patterns:
|
|
289
|
+
if pattern in agent_name:
|
|
290
|
+
self.logger.debug(f"Excluding '{agent_name}' due to pattern '{pattern}'")
|
|
291
|
+
excluded = True
|
|
292
|
+
break
|
|
293
|
+
|
|
294
|
+
# Check environment exclusions
|
|
295
|
+
if not excluded and agent_name in env_exclusions:
|
|
296
|
+
self.logger.debug(f"Excluding '{agent_name}' due to {environment} environment")
|
|
297
|
+
excluded = True
|
|
298
|
+
|
|
299
|
+
if not excluded:
|
|
300
|
+
filtered_agents[agent_name] = agent_info
|
|
301
|
+
|
|
302
|
+
return filtered_agents
|
|
303
|
+
|
|
304
|
+
def compare_deployed_versions(
|
|
305
|
+
self,
|
|
306
|
+
deployed_agents_dir: Path,
|
|
307
|
+
agents_to_deploy: Dict[str, Path],
|
|
308
|
+
agent_sources: Dict[str, str]
|
|
309
|
+
) -> Dict[str, Any]:
|
|
310
|
+
"""Compare deployed agent versions with candidates for deployment.
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
deployed_agents_dir: Directory containing currently deployed agents
|
|
314
|
+
agents_to_deploy: Dictionary mapping agent names to template paths
|
|
315
|
+
agent_sources: Dictionary mapping agent names to their sources
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
Dictionary with comparison results including which agents need updates
|
|
319
|
+
"""
|
|
320
|
+
comparison_results = {
|
|
321
|
+
"needs_update": [],
|
|
322
|
+
"up_to_date": [],
|
|
323
|
+
"new_agents": [],
|
|
324
|
+
"version_upgrades": [],
|
|
325
|
+
"version_downgrades": [],
|
|
326
|
+
"source_changes": []
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
for agent_name, template_path in agents_to_deploy.items():
|
|
330
|
+
deployed_file = deployed_agents_dir / f"{agent_name}.md"
|
|
331
|
+
|
|
332
|
+
if not deployed_file.exists():
|
|
333
|
+
comparison_results["new_agents"].append({
|
|
334
|
+
"name": agent_name,
|
|
335
|
+
"source": agent_sources[agent_name],
|
|
336
|
+
"template": str(template_path)
|
|
337
|
+
})
|
|
338
|
+
comparison_results["needs_update"].append(agent_name)
|
|
339
|
+
continue
|
|
340
|
+
|
|
341
|
+
# Read template version
|
|
342
|
+
try:
|
|
343
|
+
template_data = json.loads(template_path.read_text())
|
|
344
|
+
template_version = self.version_manager.parse_version(
|
|
345
|
+
template_data.get("agent_version") or
|
|
346
|
+
template_data.get("version", "0.0.0")
|
|
347
|
+
)
|
|
348
|
+
except Exception as e:
|
|
349
|
+
self.logger.warning(f"Error reading template for '{agent_name}': {e}")
|
|
350
|
+
continue
|
|
351
|
+
|
|
352
|
+
# Read deployed version
|
|
353
|
+
try:
|
|
354
|
+
deployed_content = deployed_file.read_text()
|
|
355
|
+
deployed_version, _, _ = self.version_manager.extract_version_from_frontmatter(
|
|
356
|
+
deployed_content
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
# Extract source from deployed agent if available
|
|
360
|
+
deployed_source = "unknown"
|
|
361
|
+
if "source:" in deployed_content:
|
|
362
|
+
import re
|
|
363
|
+
source_match = re.search(r'^source:\s*(.+)$', deployed_content, re.MULTILINE)
|
|
364
|
+
if source_match:
|
|
365
|
+
deployed_source = source_match.group(1).strip()
|
|
366
|
+
except Exception as e:
|
|
367
|
+
self.logger.warning(f"Error reading deployed agent '{agent_name}': {e}")
|
|
368
|
+
comparison_results["needs_update"].append(agent_name)
|
|
369
|
+
continue
|
|
370
|
+
|
|
371
|
+
# Compare versions
|
|
372
|
+
version_comparison = self.version_manager.compare_versions(
|
|
373
|
+
template_version, deployed_version
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
if version_comparison > 0:
|
|
377
|
+
# Template version is higher
|
|
378
|
+
comparison_results["version_upgrades"].append({
|
|
379
|
+
"name": agent_name,
|
|
380
|
+
"deployed_version": self.version_manager.format_version_display(deployed_version),
|
|
381
|
+
"new_version": self.version_manager.format_version_display(template_version),
|
|
382
|
+
"source": agent_sources[agent_name],
|
|
383
|
+
"previous_source": deployed_source
|
|
384
|
+
})
|
|
385
|
+
comparison_results["needs_update"].append(agent_name)
|
|
386
|
+
|
|
387
|
+
if deployed_source != agent_sources[agent_name]:
|
|
388
|
+
comparison_results["source_changes"].append({
|
|
389
|
+
"name": agent_name,
|
|
390
|
+
"from_source": deployed_source,
|
|
391
|
+
"to_source": agent_sources[agent_name]
|
|
392
|
+
})
|
|
393
|
+
elif version_comparison < 0:
|
|
394
|
+
# Deployed version is higher (shouldn't happen with proper version management)
|
|
395
|
+
comparison_results["version_downgrades"].append({
|
|
396
|
+
"name": agent_name,
|
|
397
|
+
"deployed_version": self.version_manager.format_version_display(deployed_version),
|
|
398
|
+
"template_version": self.version_manager.format_version_display(template_version),
|
|
399
|
+
"warning": "Deployed version is higher than template"
|
|
400
|
+
})
|
|
401
|
+
# Don't add to needs_update - keep the higher version
|
|
402
|
+
else:
|
|
403
|
+
# Versions are equal
|
|
404
|
+
comparison_results["up_to_date"].append({
|
|
405
|
+
"name": agent_name,
|
|
406
|
+
"version": self.version_manager.format_version_display(deployed_version),
|
|
407
|
+
"source": agent_sources[agent_name]
|
|
408
|
+
})
|
|
409
|
+
|
|
410
|
+
# Log summary
|
|
411
|
+
self.logger.info(
|
|
412
|
+
f"Version comparison complete: "
|
|
413
|
+
f"{len(comparison_results['needs_update'])} need updates, "
|
|
414
|
+
f"{len(comparison_results['up_to_date'])} up to date, "
|
|
415
|
+
f"{len(comparison_results['new_agents'])} new agents"
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
if comparison_results["version_upgrades"]:
|
|
419
|
+
for upgrade in comparison_results["version_upgrades"]:
|
|
420
|
+
self.logger.info(
|
|
421
|
+
f" Upgrade: {upgrade['name']} "
|
|
422
|
+
f"{upgrade['deployed_version']} -> {upgrade['new_version']} "
|
|
423
|
+
f"(from {upgrade['source']})"
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
if comparison_results["source_changes"]:
|
|
427
|
+
for change in comparison_results["source_changes"]:
|
|
428
|
+
self.logger.info(
|
|
429
|
+
f" Source change: {change['name']} "
|
|
430
|
+
f"from {change['from_source']} to {change['to_source']}"
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
if comparison_results["version_downgrades"]:
|
|
434
|
+
for downgrade in comparison_results["version_downgrades"]:
|
|
435
|
+
self.logger.warning(
|
|
436
|
+
f" Warning: {downgrade['name']} deployed version "
|
|
437
|
+
f"{downgrade['deployed_version']} is higher than template "
|
|
438
|
+
f"{downgrade['template_version']}"
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
return comparison_results
|
|
@@ -7,7 +7,6 @@ from .agent_persistence_service import (
|
|
|
7
7
|
PersistenceRecord,
|
|
8
8
|
PersistenceStrategy,
|
|
9
9
|
)
|
|
10
|
-
from .analyzer import MemoryAnalyzer
|
|
11
10
|
from .content_manager import MemoryContentManager
|
|
12
11
|
from .template_generator import MemoryTemplateGenerator
|
|
13
12
|
|
|
@@ -20,5 +19,4 @@ __all__ = [
|
|
|
20
19
|
"PersistenceRecord",
|
|
21
20
|
"MemoryTemplateGenerator",
|
|
22
21
|
"MemoryContentManager",
|
|
23
|
-
"MemoryAnalyzer",
|
|
24
22
|
]
|