claude-mpm 4.2.9__py3-none-any.whl → 4.2.11__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/cli/commands/dashboard.py +59 -126
- claude_mpm/cli/commands/monitor.py +71 -212
- claude_mpm/cli/commands/run.py +33 -33
- claude_mpm/dashboard/static/css/code-tree.css +8 -16
- claude_mpm/dashboard/static/dist/components/code-tree.js +1 -1
- claude_mpm/dashboard/static/dist/components/file-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/components/unified-data-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/dashboard.js +1 -1
- claude_mpm/dashboard/static/dist/socket-client.js +1 -1
- claude_mpm/dashboard/static/js/components/code-tree.js +692 -114
- claude_mpm/dashboard/static/js/components/file-viewer.js +538 -0
- claude_mpm/dashboard/static/js/components/module-viewer.js +26 -0
- claude_mpm/dashboard/static/js/components/unified-data-viewer.js +166 -14
- claude_mpm/dashboard/static/js/dashboard.js +108 -91
- claude_mpm/dashboard/static/js/socket-client.js +9 -7
- claude_mpm/dashboard/templates/index.html +2 -7
- claude_mpm/hooks/claude_hooks/hook_handler.py +1 -11
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +54 -59
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +112 -72
- claude_mpm/services/agents/deployment/agent_template_builder.py +0 -1
- claude_mpm/services/cli/unified_dashboard_manager.py +354 -0
- claude_mpm/services/monitor/__init__.py +20 -0
- claude_mpm/services/monitor/daemon.py +256 -0
- claude_mpm/services/monitor/event_emitter.py +279 -0
- claude_mpm/services/monitor/handlers/__init__.py +20 -0
- claude_mpm/services/monitor/handlers/code_analysis.py +334 -0
- claude_mpm/services/monitor/handlers/dashboard.py +298 -0
- claude_mpm/services/monitor/handlers/hooks.py +491 -0
- claude_mpm/services/monitor/management/__init__.py +18 -0
- claude_mpm/services/monitor/management/health.py +124 -0
- claude_mpm/services/monitor/management/lifecycle.py +298 -0
- claude_mpm/services/monitor/server.py +442 -0
- claude_mpm/tools/code_tree_analyzer.py +33 -17
- {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.11.dist-info}/METADATA +1 -1
- {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.11.dist-info}/RECORD +41 -36
- claude_mpm/cli/commands/socketio_monitor.py +0 -233
- claude_mpm/scripts/socketio_daemon.py +0 -571
- claude_mpm/scripts/socketio_daemon_hardened.py +0 -937
- claude_mpm/scripts/socketio_daemon_wrapper.py +0 -78
- claude_mpm/scripts/socketio_server_manager.py +0 -349
- claude_mpm/services/cli/dashboard_launcher.py +0 -423
- claude_mpm/services/cli/socketio_manager.py +0 -595
- claude_mpm/services/dashboard/stable_server.py +0 -1020
- claude_mpm/services/socketio/monitor_server.py +0 -505
- {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.11.dist-info}/WHEEL +0 -0
- {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.11.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.11.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.11.dist-info}/top_level.txt +0 -0
|
@@ -1,505 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Lightweight MonitorServer for claude-mpm.
|
|
3
|
-
|
|
4
|
-
WHY: This module provides a minimal, independent monitoring service that:
|
|
5
|
-
- Runs as a stable background service on port 8765
|
|
6
|
-
- Only handles event collection and relay (no UI components)
|
|
7
|
-
- Has minimal dependencies and resource usage
|
|
8
|
-
- Can run as always-on background service
|
|
9
|
-
- Includes event buffering capabilities
|
|
10
|
-
- Acts as a bridge between hooks and dashboard(s)
|
|
11
|
-
|
|
12
|
-
DESIGN DECISIONS:
|
|
13
|
-
- Minimal Socket.IO server with only essential features
|
|
14
|
-
- Event buffering for reliable delivery to dashboard clients
|
|
15
|
-
- Independent lifecycle from dashboard service
|
|
16
|
-
- Configurable port with sensible defaults
|
|
17
|
-
- Health monitoring and status endpoints
|
|
18
|
-
"""
|
|
19
|
-
|
|
20
|
-
import asyncio
|
|
21
|
-
import threading
|
|
22
|
-
import time
|
|
23
|
-
from collections import deque
|
|
24
|
-
from datetime import datetime
|
|
25
|
-
from typing import Any, Dict, List, Optional, Set
|
|
26
|
-
|
|
27
|
-
try:
|
|
28
|
-
import socketio
|
|
29
|
-
from aiohttp import web
|
|
30
|
-
|
|
31
|
-
SOCKETIO_AVAILABLE = True
|
|
32
|
-
except ImportError:
|
|
33
|
-
SOCKETIO_AVAILABLE = False
|
|
34
|
-
socketio = None
|
|
35
|
-
web = None
|
|
36
|
-
|
|
37
|
-
from ...core.config import Config
|
|
38
|
-
from ...core.constants import SystemLimits
|
|
39
|
-
from ...core.logging_config import get_logger
|
|
40
|
-
from ..core.interfaces.communication import SocketIOServiceInterface
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
class MonitorServer(SocketIOServiceInterface):
|
|
44
|
-
"""Lightweight Socket.IO server for monitoring and event relay.
|
|
45
|
-
|
|
46
|
-
WHY: This server acts as a stable, lightweight background service that:
|
|
47
|
-
- Collects events from hooks and other system components
|
|
48
|
-
- Buffers events for reliable delivery
|
|
49
|
-
- Relays events to connected dashboard clients
|
|
50
|
-
- Maintains minimal resource footprint
|
|
51
|
-
- Can run independently of dashboard services
|
|
52
|
-
|
|
53
|
-
This separation allows the monitor to be a stable always-on service
|
|
54
|
-
while dashboards can come and go without affecting event collection.
|
|
55
|
-
"""
|
|
56
|
-
|
|
57
|
-
def __init__(self, host: Optional[str] = None, port: Optional[int] = None):
|
|
58
|
-
# Load configuration
|
|
59
|
-
config = Config()
|
|
60
|
-
monitor_config = config.get("monitor_server", {})
|
|
61
|
-
|
|
62
|
-
self.host = host or monitor_config.get("host", "localhost")
|
|
63
|
-
self.port = port or monitor_config.get("port", 8765)
|
|
64
|
-
self.logger = get_logger(__name__ + ".MonitorServer")
|
|
65
|
-
|
|
66
|
-
# Configuration-based settings
|
|
67
|
-
self.event_buffer_size = monitor_config.get(
|
|
68
|
-
"event_buffer_size", SystemLimits.MAX_EVENTS_BUFFER * 2
|
|
69
|
-
)
|
|
70
|
-
self.client_timeout = monitor_config.get("client_timeout", 60)
|
|
71
|
-
self.health_monitoring_enabled = monitor_config.get(
|
|
72
|
-
"enable_health_monitoring", True
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
# Server state
|
|
76
|
-
self.running = False
|
|
77
|
-
self.sio = None
|
|
78
|
-
self.app = None
|
|
79
|
-
self.runner = None
|
|
80
|
-
self.site = None
|
|
81
|
-
self.thread = None
|
|
82
|
-
self.loop = None
|
|
83
|
-
|
|
84
|
-
# Client management
|
|
85
|
-
self.connected_clients: Set[str] = set()
|
|
86
|
-
self.client_info: Dict[str, Dict[str, Any]] = {}
|
|
87
|
-
|
|
88
|
-
# Event buffering - configurable buffer size for monitor server
|
|
89
|
-
self.event_buffer = deque(maxlen=self.event_buffer_size)
|
|
90
|
-
self.buffer_lock = threading.Lock()
|
|
91
|
-
|
|
92
|
-
# Statistics
|
|
93
|
-
self.stats = {
|
|
94
|
-
"events_received": 0,
|
|
95
|
-
"events_relayed": 0,
|
|
96
|
-
"events_buffered": 0,
|
|
97
|
-
"connections_total": 0,
|
|
98
|
-
"start_time": None,
|
|
99
|
-
"clients_connected": 0,
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
# Session tracking for compatibility
|
|
103
|
-
self.session_id = None
|
|
104
|
-
self.claude_status = "unknown"
|
|
105
|
-
self.claude_pid = None
|
|
106
|
-
self.active_sessions: Dict[str, Dict[str, Any]] = {}
|
|
107
|
-
|
|
108
|
-
def start_sync(self):
|
|
109
|
-
"""Start the monitor server in a background thread."""
|
|
110
|
-
if not SOCKETIO_AVAILABLE:
|
|
111
|
-
self.logger.error("Socket.IO not available - monitor server cannot start")
|
|
112
|
-
return False
|
|
113
|
-
|
|
114
|
-
if self.running:
|
|
115
|
-
self.logger.info("Monitor server already running")
|
|
116
|
-
return True
|
|
117
|
-
|
|
118
|
-
self.logger.info(
|
|
119
|
-
f"Starting lightweight monitor server on {self.host}:{self.port}"
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
# Start server in background thread
|
|
123
|
-
self.thread = threading.Thread(target=self._run_server, daemon=True)
|
|
124
|
-
self.thread.start()
|
|
125
|
-
|
|
126
|
-
# Wait for server to start
|
|
127
|
-
max_wait = 10.0
|
|
128
|
-
wait_interval = 0.1
|
|
129
|
-
waited = 0.0
|
|
130
|
-
|
|
131
|
-
while not self.running and waited < max_wait:
|
|
132
|
-
time.sleep(wait_interval)
|
|
133
|
-
waited += wait_interval
|
|
134
|
-
|
|
135
|
-
if self.running:
|
|
136
|
-
self.stats["start_time"] = datetime.now().isoformat()
|
|
137
|
-
self.logger.info(
|
|
138
|
-
f"Monitor server started successfully on {self.host}:{self.port}"
|
|
139
|
-
)
|
|
140
|
-
return True
|
|
141
|
-
self.logger.error(f"Monitor server failed to start within {max_wait}s")
|
|
142
|
-
return False
|
|
143
|
-
|
|
144
|
-
def stop_sync(self):
|
|
145
|
-
"""Stop the monitor server."""
|
|
146
|
-
if not self.running:
|
|
147
|
-
return
|
|
148
|
-
|
|
149
|
-
self.logger.info("Stopping monitor server...")
|
|
150
|
-
self.running = False
|
|
151
|
-
|
|
152
|
-
# Stop the server
|
|
153
|
-
if self.loop and self.runner:
|
|
154
|
-
try:
|
|
155
|
-
# Schedule cleanup in the event loop
|
|
156
|
-
asyncio.run_coroutine_threadsafe(self._stop_server(), self.loop)
|
|
157
|
-
|
|
158
|
-
# Wait for thread to finish
|
|
159
|
-
if self.thread and self.thread.is_alive():
|
|
160
|
-
self.thread.join(timeout=5)
|
|
161
|
-
|
|
162
|
-
except Exception as e:
|
|
163
|
-
self.logger.error(f"Error stopping monitor server: {e}")
|
|
164
|
-
|
|
165
|
-
self.logger.info("Monitor server stopped")
|
|
166
|
-
|
|
167
|
-
def _run_server(self):
|
|
168
|
-
"""Run the server in its own event loop."""
|
|
169
|
-
try:
|
|
170
|
-
# Create new event loop for this thread
|
|
171
|
-
self.loop = asyncio.new_event_loop()
|
|
172
|
-
asyncio.set_event_loop(self.loop)
|
|
173
|
-
|
|
174
|
-
# Start the server
|
|
175
|
-
self.loop.run_until_complete(self._start_server())
|
|
176
|
-
|
|
177
|
-
except Exception as e:
|
|
178
|
-
self.logger.error(f"Error in monitor server thread: {e}")
|
|
179
|
-
self.running = False
|
|
180
|
-
|
|
181
|
-
async def _start_server(self):
|
|
182
|
-
"""Start the Socket.IO server with minimal configuration."""
|
|
183
|
-
try:
|
|
184
|
-
# Create Socket.IO server with minimal configuration
|
|
185
|
-
self.sio = socketio.AsyncServer(
|
|
186
|
-
cors_allowed_origins="*",
|
|
187
|
-
logger=False, # Minimize logging overhead
|
|
188
|
-
engineio_logger=False,
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
# Create minimal aiohttp application
|
|
192
|
-
self.app = web.Application()
|
|
193
|
-
self.sio.attach(self.app)
|
|
194
|
-
|
|
195
|
-
# Register minimal event handlers
|
|
196
|
-
self._register_events()
|
|
197
|
-
|
|
198
|
-
# Add health check endpoint
|
|
199
|
-
self.app.router.add_get("/health", self._health_check)
|
|
200
|
-
self.app.router.add_get("/status", self._status_check)
|
|
201
|
-
|
|
202
|
-
# Start the HTTP server
|
|
203
|
-
self.runner = web.AppRunner(self.app)
|
|
204
|
-
await self.runner.setup()
|
|
205
|
-
|
|
206
|
-
self.site = web.TCPSite(
|
|
207
|
-
self.runner, self.host, self.port, shutdown_timeout=1.0 # Fast shutdown
|
|
208
|
-
)
|
|
209
|
-
|
|
210
|
-
await self.site.start()
|
|
211
|
-
self.running = True
|
|
212
|
-
|
|
213
|
-
self.logger.info(f"Monitor server running on {self.host}:{self.port}")
|
|
214
|
-
|
|
215
|
-
# Keep the server running
|
|
216
|
-
while self.running:
|
|
217
|
-
await asyncio.sleep(1)
|
|
218
|
-
|
|
219
|
-
except Exception as e:
|
|
220
|
-
self.logger.error(f"Failed to start monitor server: {e}")
|
|
221
|
-
self.running = False
|
|
222
|
-
|
|
223
|
-
async def _stop_server(self):
|
|
224
|
-
"""Stop the server components."""
|
|
225
|
-
try:
|
|
226
|
-
if self.site:
|
|
227
|
-
await self.site.stop()
|
|
228
|
-
if self.runner:
|
|
229
|
-
await self.runner.cleanup()
|
|
230
|
-
except Exception as e:
|
|
231
|
-
self.logger.error(f"Error stopping server components: {e}")
|
|
232
|
-
|
|
233
|
-
def _register_events(self):
|
|
234
|
-
"""Register minimal Socket.IO events for monitoring."""
|
|
235
|
-
|
|
236
|
-
@self.sio.event
|
|
237
|
-
async def connect(sid, environ):
|
|
238
|
-
"""Handle client connection."""
|
|
239
|
-
self.connected_clients.add(sid)
|
|
240
|
-
self.client_info[sid] = {
|
|
241
|
-
"connected_at": datetime.now().isoformat(),
|
|
242
|
-
"client_type": "dashboard", # Assume dashboard clients
|
|
243
|
-
}
|
|
244
|
-
self.stats["connections_total"] += 1
|
|
245
|
-
self.stats["clients_connected"] = len(self.connected_clients)
|
|
246
|
-
|
|
247
|
-
self.logger.info(f"Dashboard client connected: {sid}")
|
|
248
|
-
|
|
249
|
-
# Send buffered events to new client
|
|
250
|
-
await self._send_buffered_events(sid)
|
|
251
|
-
|
|
252
|
-
@self.sio.event
|
|
253
|
-
async def disconnect(sid):
|
|
254
|
-
"""Handle client disconnection."""
|
|
255
|
-
self.connected_clients.discard(sid)
|
|
256
|
-
self.client_info.pop(sid, None)
|
|
257
|
-
self.stats["clients_connected"] = len(self.connected_clients)
|
|
258
|
-
|
|
259
|
-
self.logger.info(f"Dashboard client disconnected: {sid}")
|
|
260
|
-
|
|
261
|
-
@self.sio.event
|
|
262
|
-
async def get_status(sid):
|
|
263
|
-
"""Handle status request from client."""
|
|
264
|
-
status_data = {
|
|
265
|
-
"server_type": "monitor",
|
|
266
|
-
"running": self.running,
|
|
267
|
-
"port": self.port,
|
|
268
|
-
"connected_clients": len(self.connected_clients),
|
|
269
|
-
"stats": self.stats,
|
|
270
|
-
"active_sessions": list(self.active_sessions.values()),
|
|
271
|
-
}
|
|
272
|
-
await self.sio.emit("status_response", status_data, room=sid)
|
|
273
|
-
|
|
274
|
-
async def _send_buffered_events(self, client_id: str):
|
|
275
|
-
"""Send buffered events to a newly connected client."""
|
|
276
|
-
with self.buffer_lock:
|
|
277
|
-
if self.event_buffer:
|
|
278
|
-
self.logger.info(
|
|
279
|
-
f"Sending {len(self.event_buffer)} buffered events to {client_id}"
|
|
280
|
-
)
|
|
281
|
-
for event in list(self.event_buffer):
|
|
282
|
-
try:
|
|
283
|
-
await self.sio.emit(
|
|
284
|
-
event["type"], event["data"], room=client_id
|
|
285
|
-
)
|
|
286
|
-
except Exception as e:
|
|
287
|
-
self.logger.error(
|
|
288
|
-
f"Error sending buffered event to {client_id}: {e}"
|
|
289
|
-
)
|
|
290
|
-
|
|
291
|
-
async def _health_check(self, request):
|
|
292
|
-
"""Health check endpoint."""
|
|
293
|
-
return web.json_response(
|
|
294
|
-
{
|
|
295
|
-
"status": "healthy" if self.running else "unhealthy",
|
|
296
|
-
"service": "monitor-server",
|
|
297
|
-
"port": self.port,
|
|
298
|
-
"clients": len(self.connected_clients),
|
|
299
|
-
}
|
|
300
|
-
)
|
|
301
|
-
|
|
302
|
-
async def _status_check(self, request):
|
|
303
|
-
"""Detailed status endpoint."""
|
|
304
|
-
return web.json_response(
|
|
305
|
-
{
|
|
306
|
-
"running": self.running,
|
|
307
|
-
"port": self.port,
|
|
308
|
-
"host": self.host,
|
|
309
|
-
"clients_connected": len(self.connected_clients),
|
|
310
|
-
"stats": self.stats,
|
|
311
|
-
"active_sessions": list(self.active_sessions.values()),
|
|
312
|
-
"buffer_size": len(self.event_buffer),
|
|
313
|
-
}
|
|
314
|
-
)
|
|
315
|
-
|
|
316
|
-
# SocketIOServiceInterface implementation
|
|
317
|
-
def broadcast_event(self, event_type: str, data: Dict[str, Any]):
|
|
318
|
-
"""Broadcast an event to all connected dashboard clients."""
|
|
319
|
-
self.stats["events_received"] += 1
|
|
320
|
-
|
|
321
|
-
# Buffer the event
|
|
322
|
-
event_data = {
|
|
323
|
-
"type": event_type,
|
|
324
|
-
"data": data,
|
|
325
|
-
"timestamp": datetime.now().isoformat(),
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
with self.buffer_lock:
|
|
329
|
-
self.event_buffer.append(event_data)
|
|
330
|
-
self.stats["events_buffered"] += 1
|
|
331
|
-
|
|
332
|
-
# Relay to connected clients if server is running
|
|
333
|
-
if self.loop and self.sio and self.connected_clients:
|
|
334
|
-
asyncio.run_coroutine_threadsafe(
|
|
335
|
-
self._relay_event(event_type, data), self.loop
|
|
336
|
-
)
|
|
337
|
-
|
|
338
|
-
async def _relay_event(self, event_type: str, data: Dict[str, Any]):
|
|
339
|
-
"""Relay event to all connected dashboard clients."""
|
|
340
|
-
if not self.connected_clients:
|
|
341
|
-
return
|
|
342
|
-
|
|
343
|
-
try:
|
|
344
|
-
await self.sio.emit(event_type, data)
|
|
345
|
-
self.stats["events_relayed"] += 1
|
|
346
|
-
except Exception as e:
|
|
347
|
-
self.logger.error(f"Error relaying event {event_type}: {e}")
|
|
348
|
-
|
|
349
|
-
def send_to_client(
|
|
350
|
-
self, client_id: str, event_type: str, data: Dict[str, Any]
|
|
351
|
-
) -> bool:
|
|
352
|
-
"""Send an event to a specific dashboard client."""
|
|
353
|
-
if not self.loop or client_id not in self.connected_clients:
|
|
354
|
-
return False
|
|
355
|
-
|
|
356
|
-
try:
|
|
357
|
-
asyncio.run_coroutine_threadsafe(
|
|
358
|
-
self.sio.emit(event_type, data, room=client_id), self.loop
|
|
359
|
-
)
|
|
360
|
-
return True
|
|
361
|
-
except Exception as e:
|
|
362
|
-
self.logger.error(f"Error sending to client {client_id}: {e}")
|
|
363
|
-
return False
|
|
364
|
-
|
|
365
|
-
def get_connection_count(self) -> int:
|
|
366
|
-
"""Get number of connected dashboard clients."""
|
|
367
|
-
return len(self.connected_clients)
|
|
368
|
-
|
|
369
|
-
def is_running(self) -> bool:
|
|
370
|
-
"""Check if monitor server is running."""
|
|
371
|
-
return self.running
|
|
372
|
-
|
|
373
|
-
def get_stats(self) -> Dict[str, Any]:
|
|
374
|
-
"""Get monitor server statistics."""
|
|
375
|
-
return {
|
|
376
|
-
**self.stats,
|
|
377
|
-
"clients_connected": len(self.connected_clients),
|
|
378
|
-
"buffer_size": len(self.event_buffer),
|
|
379
|
-
"uptime": (
|
|
380
|
-
(
|
|
381
|
-
datetime.now() - datetime.fromisoformat(self.stats["start_time"])
|
|
382
|
-
).total_seconds()
|
|
383
|
-
if self.stats["start_time"]
|
|
384
|
-
else 0
|
|
385
|
-
),
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
# Session tracking methods for compatibility with existing hooks
|
|
389
|
-
def session_started(self, session_id: str, launch_method: str, working_dir: str):
|
|
390
|
-
"""Track session start."""
|
|
391
|
-
self.session_id = session_id
|
|
392
|
-
self.active_sessions[session_id] = {
|
|
393
|
-
"session_id": session_id,
|
|
394
|
-
"start_time": datetime.now().isoformat(),
|
|
395
|
-
"agent": "pm",
|
|
396
|
-
"status": "active",
|
|
397
|
-
"launch_method": launch_method,
|
|
398
|
-
"working_dir": working_dir,
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
self.broadcast_event(
|
|
402
|
-
"session_started",
|
|
403
|
-
{
|
|
404
|
-
"session_id": session_id,
|
|
405
|
-
"launch_method": launch_method,
|
|
406
|
-
"working_dir": working_dir,
|
|
407
|
-
},
|
|
408
|
-
)
|
|
409
|
-
|
|
410
|
-
def session_ended(self):
|
|
411
|
-
"""Track session end."""
|
|
412
|
-
if self.session_id and self.session_id in self.active_sessions:
|
|
413
|
-
self.active_sessions.pop(self.session_id)
|
|
414
|
-
self.broadcast_event("session_ended", {"session_id": self.session_id})
|
|
415
|
-
|
|
416
|
-
def claude_status_changed(
|
|
417
|
-
self, status: str, pid: Optional[int] = None, message: str = ""
|
|
418
|
-
):
|
|
419
|
-
"""Track Claude status changes."""
|
|
420
|
-
self.claude_status = status
|
|
421
|
-
self.claude_pid = pid
|
|
422
|
-
self.broadcast_event(
|
|
423
|
-
"claude_status", {"status": status, "pid": pid, "message": message}
|
|
424
|
-
)
|
|
425
|
-
|
|
426
|
-
def claude_output(self, content: str, stream: str = "stdout"):
|
|
427
|
-
"""Relay Claude output."""
|
|
428
|
-
self.broadcast_event("claude_output", {"content": content, "stream": stream})
|
|
429
|
-
|
|
430
|
-
def agent_delegated(self, agent: str, task: str, status: str = "started"):
|
|
431
|
-
"""Track agent delegation."""
|
|
432
|
-
if self.session_id and self.session_id in self.active_sessions:
|
|
433
|
-
self.active_sessions[self.session_id]["agent"] = agent
|
|
434
|
-
self.active_sessions[self.session_id]["status"] = status
|
|
435
|
-
|
|
436
|
-
self.broadcast_event(
|
|
437
|
-
"agent_delegated", {"agent": agent, "task": task, "status": status}
|
|
438
|
-
)
|
|
439
|
-
|
|
440
|
-
def todo_updated(self, todos: List[Dict[str, Any]]):
|
|
441
|
-
"""Relay todo updates."""
|
|
442
|
-
self.broadcast_event("todos_updated", {"todos": todos})
|
|
443
|
-
|
|
444
|
-
def ticket_created(self, ticket_id: str, title: str, priority: str = "medium"):
|
|
445
|
-
"""Relay ticket creation."""
|
|
446
|
-
self.broadcast_event(
|
|
447
|
-
"ticket_created",
|
|
448
|
-
{"ticket_id": ticket_id, "title": title, "priority": priority},
|
|
449
|
-
)
|
|
450
|
-
|
|
451
|
-
def memory_loaded(self, agent_id: str, memory_size: int, sections_count: int):
|
|
452
|
-
"""Relay memory loaded event."""
|
|
453
|
-
self.broadcast_event(
|
|
454
|
-
"memory_loaded",
|
|
455
|
-
{
|
|
456
|
-
"agent_id": agent_id,
|
|
457
|
-
"memory_size": memory_size,
|
|
458
|
-
"sections_count": sections_count,
|
|
459
|
-
},
|
|
460
|
-
)
|
|
461
|
-
|
|
462
|
-
def memory_created(self, agent_id: str, template_type: str):
|
|
463
|
-
"""Relay memory created event."""
|
|
464
|
-
self.broadcast_event(
|
|
465
|
-
"memory_created", {"agent_id": agent_id, "template_type": template_type}
|
|
466
|
-
)
|
|
467
|
-
|
|
468
|
-
def memory_updated(
|
|
469
|
-
self, agent_id: str, learning_type: str, content: str, section: str
|
|
470
|
-
):
|
|
471
|
-
"""Relay memory update event."""
|
|
472
|
-
self.broadcast_event(
|
|
473
|
-
"memory_updated",
|
|
474
|
-
{
|
|
475
|
-
"agent_id": agent_id,
|
|
476
|
-
"learning_type": learning_type,
|
|
477
|
-
"content": content,
|
|
478
|
-
"section": section,
|
|
479
|
-
},
|
|
480
|
-
)
|
|
481
|
-
|
|
482
|
-
def memory_injected(self, agent_id: str, context_size: int):
|
|
483
|
-
"""Relay memory injection event."""
|
|
484
|
-
self.broadcast_event(
|
|
485
|
-
"memory_injected", {"agent_id": agent_id, "context_size": context_size}
|
|
486
|
-
)
|
|
487
|
-
|
|
488
|
-
def get_active_sessions(self) -> List[Dict[str, Any]]:
|
|
489
|
-
"""Get list of active sessions."""
|
|
490
|
-
# Clean up old sessions (older than 1 hour)
|
|
491
|
-
cutoff_time = datetime.now().timestamp() - 3600
|
|
492
|
-
sessions_to_remove = []
|
|
493
|
-
|
|
494
|
-
for session_id, session_data in self.active_sessions.items():
|
|
495
|
-
try:
|
|
496
|
-
start_time = datetime.fromisoformat(session_data["start_time"])
|
|
497
|
-
if start_time.timestamp() < cutoff_time:
|
|
498
|
-
sessions_to_remove.append(session_id)
|
|
499
|
-
except:
|
|
500
|
-
pass
|
|
501
|
-
|
|
502
|
-
for session_id in sessions_to_remove:
|
|
503
|
-
del self.active_sessions[session_id]
|
|
504
|
-
|
|
505
|
-
return list(self.active_sessions.values())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|