claude-mpm 3.3.0__py3-none-any.whl → 3.4.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.
- claude_mpm/agents/templates/data_engineer.json +1 -1
- claude_mpm/agents/templates/documentation.json +1 -1
- claude_mpm/agents/templates/engineer.json +1 -1
- claude_mpm/agents/templates/ops.json +1 -1
- claude_mpm/agents/templates/pm.json +1 -1
- claude_mpm/agents/templates/qa.json +1 -1
- claude_mpm/agents/templates/research.json +1 -1
- claude_mpm/agents/templates/security.json +1 -1
- claude_mpm/agents/templates/test_integration.json +112 -0
- claude_mpm/agents/templates/version_control.json +1 -1
- claude_mpm/cli/commands/memory.py +749 -26
- claude_mpm/cli/commands/run.py +115 -14
- claude_mpm/cli/parser.py +89 -1
- claude_mpm/constants.py +6 -0
- claude_mpm/core/claude_runner.py +74 -11
- claude_mpm/core/config.py +1 -1
- claude_mpm/core/session_manager.py +46 -0
- claude_mpm/core/simple_runner.py +74 -11
- claude_mpm/hooks/builtin/mpm_command_hook.py +5 -5
- claude_mpm/hooks/claude_hooks/hook_handler.py +213 -30
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +9 -2
- claude_mpm/hooks/memory_integration_hook.py +51 -5
- claude_mpm/services/__init__.py +23 -5
- claude_mpm/services/agent_memory_manager.py +800 -71
- claude_mpm/services/memory_builder.py +823 -0
- claude_mpm/services/memory_optimizer.py +619 -0
- claude_mpm/services/memory_router.py +445 -0
- claude_mpm/services/project_analyzer.py +771 -0
- claude_mpm/services/socketio_server.py +649 -45
- claude_mpm/services/version_control/git_operations.py +26 -0
- claude_mpm-3.4.0.dist-info/METADATA +183 -0
- {claude_mpm-3.3.0.dist-info → claude_mpm-3.4.0.dist-info}/RECORD +36 -52
- claude_mpm/agents/agent-template.yaml +0 -83
- claude_mpm/agents/templates/test-integration-agent.md +0 -34
- claude_mpm/agents/test_fix_deployment/.claude-pm/config/project.json +0 -6
- claude_mpm/cli/README.md +0 -109
- claude_mpm/cli_module/refactoring_guide.md +0 -253
- claude_mpm/core/agent_registry.py.bak +0 -312
- claude_mpm/core/base_service.py.bak +0 -406
- claude_mpm/core/websocket_handler.py +0 -233
- claude_mpm/hooks/README.md +0 -97
- claude_mpm/orchestration/SUBPROCESS_DESIGN.md +0 -66
- claude_mpm/schemas/README_SECURITY.md +0 -92
- claude_mpm/schemas/agent_schema.json +0 -395
- claude_mpm/schemas/agent_schema_documentation.md +0 -181
- claude_mpm/schemas/agent_schema_security_notes.md +0 -165
- claude_mpm/schemas/examples/standard_workflow.json +0 -505
- claude_mpm/schemas/ticket_workflow_documentation.md +0 -482
- claude_mpm/schemas/ticket_workflow_schema.json +0 -590
- claude_mpm/services/framework_claude_md_generator/README.md +0 -92
- claude_mpm/services/parent_directory_manager/README.md +0 -83
- claude_mpm/services/version_control/VERSION +0 -1
- claude_mpm/services/websocket_server.py +0 -376
- claude_mpm-3.3.0.dist-info/METADATA +0 -432
- {claude_mpm-3.3.0.dist-info → claude_mpm-3.4.0.dist-info}/WHEEL +0 -0
- {claude_mpm-3.3.0.dist-info → claude_mpm-3.4.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-3.3.0.dist-info → claude_mpm-3.4.0.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-3.3.0.dist-info → claude_mpm-3.4.0.dist-info}/top_level.txt +0 -0
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
# Parent Directory Manager
|
|
2
|
-
|
|
3
|
-
This directory contains the modularized components of the Parent Directory Manager service.
|
|
4
|
-
|
|
5
|
-
## Module Structure
|
|
6
|
-
|
|
7
|
-
The Parent Directory Manager has been refactored into specialized modules following the single-responsibility principle:
|
|
8
|
-
|
|
9
|
-
### Core Module
|
|
10
|
-
- `__init__.py` - Main ParentDirectoryManager class that orchestrates all operations
|
|
11
|
-
|
|
12
|
-
### Specialized Modules
|
|
13
|
-
|
|
14
|
-
1. **backup_manager.py** - Handles backup operations
|
|
15
|
-
- Creates backups of files before modifications
|
|
16
|
-
- Manages backup retention and cleanup
|
|
17
|
-
- Handles framework template backup protection
|
|
18
|
-
|
|
19
|
-
2. **template_deployer.py** - Manages template deployment
|
|
20
|
-
- Handles version comparison and deployment decisions
|
|
21
|
-
- Renders templates with variable substitution
|
|
22
|
-
- Integrates with framework template generator
|
|
23
|
-
|
|
24
|
-
3. **framework_protector.py** - Framework protection mechanisms
|
|
25
|
-
- Protects critical framework files
|
|
26
|
-
- Manages protection rules and validation
|
|
27
|
-
|
|
28
|
-
4. **version_control_helper.py** - Version control integration
|
|
29
|
-
- Git operations and version control checks
|
|
30
|
-
- Branch management support
|
|
31
|
-
|
|
32
|
-
5. **deduplication_manager.py** - CLAUDE.md deduplication
|
|
33
|
-
- Detects and removes duplicate CLAUDE.md files
|
|
34
|
-
- Manages deduplication hierarchy and precedence
|
|
35
|
-
|
|
36
|
-
6. **operations.py** (formerly parent_directory_operations.py) - Directory operations
|
|
37
|
-
- Auto-detection of parent directory contexts
|
|
38
|
-
- Directory registration and management
|
|
39
|
-
|
|
40
|
-
7. **config_manager.py** - Configuration management
|
|
41
|
-
- Manages directory configurations
|
|
42
|
-
- Handles registration and configuration persistence
|
|
43
|
-
|
|
44
|
-
8. **state_manager.py** - State and lifecycle management
|
|
45
|
-
- Initialization and cleanup operations
|
|
46
|
-
- Operation history tracking
|
|
47
|
-
- Logging and error handling
|
|
48
|
-
|
|
49
|
-
9. **validation_manager.py** - Validation operations
|
|
50
|
-
- Template validation
|
|
51
|
-
- Compatibility checking
|
|
52
|
-
- Directory status validation
|
|
53
|
-
|
|
54
|
-
10. **version_manager.py** - Version tracking
|
|
55
|
-
- Subsystem version management
|
|
56
|
-
- Version compatibility validation
|
|
57
|
-
- Version reporting
|
|
58
|
-
|
|
59
|
-
## Usage
|
|
60
|
-
|
|
61
|
-
The main entry point remains the `ParentDirectoryManager` class, which delegates to these specialized modules:
|
|
62
|
-
|
|
63
|
-
```python
|
|
64
|
-
from claude_pm.services.parent_directory_manager import ParentDirectoryManager
|
|
65
|
-
|
|
66
|
-
# Initialize the manager
|
|
67
|
-
manager = ParentDirectoryManager()
|
|
68
|
-
|
|
69
|
-
# All public APIs remain the same
|
|
70
|
-
await manager.deploy_framework_template(target_dir)
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## Backward Compatibility
|
|
74
|
-
|
|
75
|
-
A stub file at `claude_pm/services/parent_directory_manager.py` ensures backward compatibility for existing imports.
|
|
76
|
-
|
|
77
|
-
## Design Principles
|
|
78
|
-
|
|
79
|
-
1. **Single Responsibility** - Each module has a focused purpose
|
|
80
|
-
2. **Delegation Pattern** - Main class delegates to specialized modules
|
|
81
|
-
3. **Dependency Injection** - Modules receive dependencies via constructor
|
|
82
|
-
4. **Async Support** - All operations support async/await patterns
|
|
83
|
-
5. **Comprehensive Logging** - Each module uses consistent logging
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.9.0
|
|
@@ -1,376 +0,0 @@
|
|
|
1
|
-
"""WebSocket server for real-time monitoring of Claude MPM sessions."""
|
|
2
|
-
|
|
3
|
-
import asyncio
|
|
4
|
-
import json
|
|
5
|
-
import logging
|
|
6
|
-
import os
|
|
7
|
-
import subprocess
|
|
8
|
-
import threading
|
|
9
|
-
import time
|
|
10
|
-
from datetime import datetime
|
|
11
|
-
from typing import Set, Dict, Any, Optional, List
|
|
12
|
-
from collections import deque
|
|
13
|
-
|
|
14
|
-
try:
|
|
15
|
-
import websockets
|
|
16
|
-
from websockets.server import WebSocketServerProtocol
|
|
17
|
-
WEBSOCKETS_AVAILABLE = True
|
|
18
|
-
except ImportError:
|
|
19
|
-
WEBSOCKETS_AVAILABLE = False
|
|
20
|
-
websockets = None
|
|
21
|
-
WebSocketServerProtocol = None
|
|
22
|
-
|
|
23
|
-
from ..core.logger import get_logger
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class WebSocketServer:
|
|
27
|
-
"""WebSocket server for broadcasting Claude MPM events."""
|
|
28
|
-
|
|
29
|
-
def __init__(self, host: str = "localhost", port: int = 8765):
|
|
30
|
-
self.host = host
|
|
31
|
-
self.port = port
|
|
32
|
-
self.logger = get_logger("websocket_server")
|
|
33
|
-
self.clients: Set[WebSocketServerProtocol] = set() if WEBSOCKETS_AVAILABLE else set()
|
|
34
|
-
self.event_history: deque = deque(maxlen=1000) # Keep last 1000 events
|
|
35
|
-
self.server = None
|
|
36
|
-
self.loop = None
|
|
37
|
-
self.thread = None
|
|
38
|
-
self.running = False
|
|
39
|
-
|
|
40
|
-
# Session state
|
|
41
|
-
self.session_id = None
|
|
42
|
-
self.session_start = None
|
|
43
|
-
self.claude_status = "stopped"
|
|
44
|
-
self.claude_pid = None
|
|
45
|
-
|
|
46
|
-
if not WEBSOCKETS_AVAILABLE:
|
|
47
|
-
self.logger.warning("WebSocket support not available. Install 'websockets' package to enable.")
|
|
48
|
-
|
|
49
|
-
def start(self):
|
|
50
|
-
"""Start the WebSocket server in a background thread."""
|
|
51
|
-
if not WEBSOCKETS_AVAILABLE:
|
|
52
|
-
self.logger.debug("WebSocket server skipped - websockets package not installed")
|
|
53
|
-
return
|
|
54
|
-
|
|
55
|
-
if self.running:
|
|
56
|
-
return
|
|
57
|
-
|
|
58
|
-
self.running = True
|
|
59
|
-
self.thread = threading.Thread(target=self._run_server, daemon=True)
|
|
60
|
-
self.thread.start()
|
|
61
|
-
self.logger.info(f"WebSocket server starting on ws://{self.host}:{self.port}")
|
|
62
|
-
|
|
63
|
-
def stop(self):
|
|
64
|
-
"""Stop the WebSocket server."""
|
|
65
|
-
self.running = False
|
|
66
|
-
if self.loop:
|
|
67
|
-
asyncio.run_coroutine_threadsafe(self._shutdown(), self.loop)
|
|
68
|
-
if self.thread:
|
|
69
|
-
self.thread.join(timeout=5)
|
|
70
|
-
self.logger.info("WebSocket server stopped")
|
|
71
|
-
|
|
72
|
-
def _run_server(self):
|
|
73
|
-
"""Run the server event loop."""
|
|
74
|
-
self.loop = asyncio.new_event_loop()
|
|
75
|
-
asyncio.set_event_loop(self.loop)
|
|
76
|
-
|
|
77
|
-
try:
|
|
78
|
-
self.loop.run_until_complete(self._serve())
|
|
79
|
-
except Exception as e:
|
|
80
|
-
self.logger.error(f"WebSocket server error: {e}")
|
|
81
|
-
finally:
|
|
82
|
-
self.loop.close()
|
|
83
|
-
|
|
84
|
-
async def _serve(self):
|
|
85
|
-
"""Start the WebSocket server."""
|
|
86
|
-
async with websockets.serve(self._handle_client, self.host, self.port):
|
|
87
|
-
self.logger.info(f"WebSocket server listening on ws://{self.host}:{self.port}")
|
|
88
|
-
while self.running:
|
|
89
|
-
await asyncio.sleep(0.1)
|
|
90
|
-
|
|
91
|
-
async def _shutdown(self):
|
|
92
|
-
"""Shutdown the server."""
|
|
93
|
-
# Close all client connections
|
|
94
|
-
if self.clients:
|
|
95
|
-
await asyncio.gather(
|
|
96
|
-
*[client.close() for client in self.clients],
|
|
97
|
-
return_exceptions=True
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
async def _handle_client(self, websocket: WebSocketServerProtocol, path: str):
|
|
101
|
-
"""Handle a new client connection."""
|
|
102
|
-
self.clients.add(websocket)
|
|
103
|
-
client_addr = websocket.remote_address
|
|
104
|
-
self.logger.info(f"Client connected from {client_addr}")
|
|
105
|
-
|
|
106
|
-
try:
|
|
107
|
-
# Send current status
|
|
108
|
-
await self._send_current_status(websocket)
|
|
109
|
-
|
|
110
|
-
# Handle client messages
|
|
111
|
-
async for message in websocket:
|
|
112
|
-
try:
|
|
113
|
-
data = json.loads(message)
|
|
114
|
-
await self._handle_command(websocket, data)
|
|
115
|
-
except json.JSONDecodeError:
|
|
116
|
-
await websocket.send(json.dumps({
|
|
117
|
-
"type": "error",
|
|
118
|
-
"data": {"message": "Invalid JSON"}
|
|
119
|
-
}))
|
|
120
|
-
|
|
121
|
-
except websockets.exceptions.ConnectionClosed:
|
|
122
|
-
pass
|
|
123
|
-
finally:
|
|
124
|
-
self.clients.remove(websocket)
|
|
125
|
-
self.logger.info(f"Client disconnected from {client_addr}")
|
|
126
|
-
|
|
127
|
-
async def _send_current_status(self, websocket: WebSocketServerProtocol):
|
|
128
|
-
"""Send current system status to a client."""
|
|
129
|
-
status = {
|
|
130
|
-
"type": "system.status",
|
|
131
|
-
"timestamp": datetime.utcnow().isoformat() + "Z",
|
|
132
|
-
"data": {
|
|
133
|
-
"session_id": self.session_id,
|
|
134
|
-
"session_start": self.session_start,
|
|
135
|
-
"claude_status": self.claude_status,
|
|
136
|
-
"claude_pid": self.claude_pid,
|
|
137
|
-
"connected_clients": len(self.clients),
|
|
138
|
-
"websocket_port": self.port,
|
|
139
|
-
"instance_info": {
|
|
140
|
-
"port": self.port,
|
|
141
|
-
"host": self.host,
|
|
142
|
-
"working_dir": os.getcwd() if self.session_id else None
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
await websocket.send(json.dumps(status))
|
|
147
|
-
|
|
148
|
-
async def _handle_command(self, websocket: WebSocketServerProtocol, data: Dict[str, Any]):
|
|
149
|
-
"""Handle commands from clients."""
|
|
150
|
-
command = data.get("command")
|
|
151
|
-
|
|
152
|
-
if command == "get_status":
|
|
153
|
-
await self._send_current_status(websocket)
|
|
154
|
-
|
|
155
|
-
elif command == "get_history":
|
|
156
|
-
# Send recent events
|
|
157
|
-
params = data.get("params", {})
|
|
158
|
-
event_types = params.get("event_types", [])
|
|
159
|
-
limit = min(params.get("limit", 100), len(self.event_history))
|
|
160
|
-
|
|
161
|
-
history = []
|
|
162
|
-
for event in reversed(self.event_history):
|
|
163
|
-
if not event_types or event["type"] in event_types:
|
|
164
|
-
history.append(event)
|
|
165
|
-
if len(history) >= limit:
|
|
166
|
-
break
|
|
167
|
-
|
|
168
|
-
await websocket.send(json.dumps({
|
|
169
|
-
"type": "history",
|
|
170
|
-
"data": {"events": list(reversed(history))}
|
|
171
|
-
}))
|
|
172
|
-
|
|
173
|
-
elif command == "subscribe":
|
|
174
|
-
# For now, all clients get all events
|
|
175
|
-
await websocket.send(json.dumps({
|
|
176
|
-
"type": "subscribed",
|
|
177
|
-
"data": {"channels": data.get("channels", ["*"])}
|
|
178
|
-
}))
|
|
179
|
-
|
|
180
|
-
def broadcast_event(self, event_type: str, data: Dict[str, Any]):
|
|
181
|
-
"""Broadcast an event to all connected clients."""
|
|
182
|
-
if not WEBSOCKETS_AVAILABLE:
|
|
183
|
-
return
|
|
184
|
-
|
|
185
|
-
event = {
|
|
186
|
-
"type": event_type,
|
|
187
|
-
"timestamp": datetime.utcnow().isoformat() + "Z",
|
|
188
|
-
"data": data
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
# Store in history
|
|
192
|
-
self.event_history.append(event)
|
|
193
|
-
|
|
194
|
-
# Broadcast to clients
|
|
195
|
-
if self.clients and self.loop:
|
|
196
|
-
asyncio.run_coroutine_threadsafe(
|
|
197
|
-
self._broadcast(json.dumps(event)),
|
|
198
|
-
self.loop
|
|
199
|
-
)
|
|
200
|
-
|
|
201
|
-
async def _broadcast(self, message: str):
|
|
202
|
-
"""Send a message to all connected clients."""
|
|
203
|
-
if self.clients:
|
|
204
|
-
# Send to all clients concurrently
|
|
205
|
-
await asyncio.gather(
|
|
206
|
-
*[client.send(message) for client in self.clients],
|
|
207
|
-
return_exceptions=True
|
|
208
|
-
)
|
|
209
|
-
|
|
210
|
-
# Convenience methods for common events
|
|
211
|
-
|
|
212
|
-
def _get_git_branch(self, working_dir: str) -> str:
|
|
213
|
-
"""Get the current git branch for the working directory."""
|
|
214
|
-
try:
|
|
215
|
-
result = subprocess.run(
|
|
216
|
-
["git", "rev-parse", "--abbrev-ref", "HEAD"],
|
|
217
|
-
cwd=working_dir,
|
|
218
|
-
capture_output=True,
|
|
219
|
-
text=True,
|
|
220
|
-
timeout=2
|
|
221
|
-
)
|
|
222
|
-
if result.returncode == 0:
|
|
223
|
-
return result.stdout.strip()
|
|
224
|
-
except Exception:
|
|
225
|
-
pass
|
|
226
|
-
return "not a git repo"
|
|
227
|
-
|
|
228
|
-
def session_started(self, session_id: str, launch_method: str, working_dir: str):
|
|
229
|
-
"""Notify that a session has started."""
|
|
230
|
-
self.session_id = session_id
|
|
231
|
-
self.session_start = datetime.utcnow().isoformat() + "Z"
|
|
232
|
-
|
|
233
|
-
# Get git branch if in a git repo
|
|
234
|
-
git_branch = self._get_git_branch(working_dir)
|
|
235
|
-
|
|
236
|
-
self.broadcast_event("session.start", {
|
|
237
|
-
"session_id": session_id,
|
|
238
|
-
"start_time": self.session_start,
|
|
239
|
-
"launch_method": launch_method,
|
|
240
|
-
"working_directory": working_dir,
|
|
241
|
-
"git_branch": git_branch,
|
|
242
|
-
"websocket_port": self.port,
|
|
243
|
-
"instance_info": {
|
|
244
|
-
"port": self.port,
|
|
245
|
-
"host": self.host,
|
|
246
|
-
"working_dir": working_dir
|
|
247
|
-
}
|
|
248
|
-
})
|
|
249
|
-
|
|
250
|
-
def session_ended(self):
|
|
251
|
-
"""Notify that a session has ended."""
|
|
252
|
-
if self.session_id:
|
|
253
|
-
duration = None
|
|
254
|
-
if self.session_start:
|
|
255
|
-
start = datetime.fromisoformat(self.session_start.replace("Z", "+00:00"))
|
|
256
|
-
duration = (datetime.utcnow() - start.replace(tzinfo=None)).total_seconds()
|
|
257
|
-
|
|
258
|
-
self.broadcast_event("session.end", {
|
|
259
|
-
"session_id": self.session_id,
|
|
260
|
-
"end_time": datetime.utcnow().isoformat() + "Z",
|
|
261
|
-
"duration_seconds": duration
|
|
262
|
-
})
|
|
263
|
-
|
|
264
|
-
self.session_id = None
|
|
265
|
-
self.session_start = None
|
|
266
|
-
|
|
267
|
-
def claude_status_changed(self, status: str, pid: Optional[int] = None, message: str = ""):
|
|
268
|
-
"""Notify Claude status change."""
|
|
269
|
-
self.claude_status = status
|
|
270
|
-
self.claude_pid = pid
|
|
271
|
-
self.broadcast_event("claude.status", {
|
|
272
|
-
"status": status,
|
|
273
|
-
"pid": pid,
|
|
274
|
-
"message": message
|
|
275
|
-
})
|
|
276
|
-
|
|
277
|
-
def claude_output(self, content: str, stream: str = "stdout"):
|
|
278
|
-
"""Broadcast Claude output."""
|
|
279
|
-
self.broadcast_event("claude.output", {
|
|
280
|
-
"content": content,
|
|
281
|
-
"stream": stream
|
|
282
|
-
})
|
|
283
|
-
|
|
284
|
-
def agent_delegated(self, agent: str, task: str, status: str = "started"):
|
|
285
|
-
"""Notify agent delegation."""
|
|
286
|
-
self.broadcast_event("agent.delegation", {
|
|
287
|
-
"agent": agent,
|
|
288
|
-
"task": task,
|
|
289
|
-
"status": status,
|
|
290
|
-
"timestamp": datetime.utcnow().isoformat() + "Z"
|
|
291
|
-
})
|
|
292
|
-
|
|
293
|
-
def todo_updated(self, todos: List[Dict[str, Any]]):
|
|
294
|
-
"""Notify todo list update."""
|
|
295
|
-
stats = {
|
|
296
|
-
"total": len(todos),
|
|
297
|
-
"completed": sum(1 for t in todos if t.get("status") == "completed"),
|
|
298
|
-
"in_progress": sum(1 for t in todos if t.get("status") == "in_progress"),
|
|
299
|
-
"pending": sum(1 for t in todos if t.get("status") == "pending")
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
self.broadcast_event("todo.update", {
|
|
303
|
-
"todos": todos,
|
|
304
|
-
"stats": stats
|
|
305
|
-
})
|
|
306
|
-
|
|
307
|
-
def ticket_created(self, ticket_id: str, title: str, priority: str = "medium"):
|
|
308
|
-
"""Notify ticket creation."""
|
|
309
|
-
self.broadcast_event("ticket.created", {
|
|
310
|
-
"id": ticket_id,
|
|
311
|
-
"title": title,
|
|
312
|
-
"priority": priority,
|
|
313
|
-
"created_at": datetime.utcnow().isoformat() + "Z"
|
|
314
|
-
})
|
|
315
|
-
|
|
316
|
-
def memory_loaded(self, agent_id: str, memory_size: int, sections_count: int):
|
|
317
|
-
"""Notify when agent memory is loaded from file."""
|
|
318
|
-
self.broadcast_event("memory:loaded", {
|
|
319
|
-
"agent_id": agent_id,
|
|
320
|
-
"memory_size": memory_size,
|
|
321
|
-
"sections_count": sections_count,
|
|
322
|
-
"timestamp": datetime.utcnow().isoformat() + "Z"
|
|
323
|
-
})
|
|
324
|
-
|
|
325
|
-
def memory_created(self, agent_id: str, template_type: str):
|
|
326
|
-
"""Notify when new agent memory is created from template."""
|
|
327
|
-
self.broadcast_event("memory:created", {
|
|
328
|
-
"agent_id": agent_id,
|
|
329
|
-
"template_type": template_type,
|
|
330
|
-
"timestamp": datetime.utcnow().isoformat() + "Z"
|
|
331
|
-
})
|
|
332
|
-
|
|
333
|
-
def memory_updated(self, agent_id: str, learning_type: str, content: str, section: str):
|
|
334
|
-
"""Notify when learning is added to agent memory."""
|
|
335
|
-
self.broadcast_event("memory:updated", {
|
|
336
|
-
"agent_id": agent_id,
|
|
337
|
-
"learning_type": learning_type,
|
|
338
|
-
"content": content,
|
|
339
|
-
"section": section,
|
|
340
|
-
"timestamp": datetime.utcnow().isoformat() + "Z"
|
|
341
|
-
})
|
|
342
|
-
|
|
343
|
-
def memory_injected(self, agent_id: str, context_size: int):
|
|
344
|
-
"""Notify when agent memory is injected into context."""
|
|
345
|
-
self.broadcast_event("memory:injected", {
|
|
346
|
-
"agent_id": agent_id,
|
|
347
|
-
"context_size": context_size,
|
|
348
|
-
"timestamp": datetime.utcnow().isoformat() + "Z"
|
|
349
|
-
})
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
# Global instance for easy access
|
|
353
|
-
_websocket_server: Optional[WebSocketServer] = None
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
def get_websocket_server() -> WebSocketServer:
|
|
357
|
-
"""Get or create the global WebSocket server instance."""
|
|
358
|
-
global _websocket_server
|
|
359
|
-
if _websocket_server is None:
|
|
360
|
-
_websocket_server = WebSocketServer()
|
|
361
|
-
return _websocket_server
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
def start_websocket_server():
|
|
365
|
-
"""Start the global WebSocket server."""
|
|
366
|
-
server = get_websocket_server()
|
|
367
|
-
server.start()
|
|
368
|
-
return server
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
def stop_websocket_server():
|
|
372
|
-
"""Stop the global WebSocket server."""
|
|
373
|
-
global _websocket_server
|
|
374
|
-
if _websocket_server:
|
|
375
|
-
_websocket_server.stop()
|
|
376
|
-
_websocket_server = None
|