claude-flow-novice 2.18.7 → 2.18.9
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.
- package/.claude/cfn-extras/skills/advanced-features/cfn-event-bus/README.md +2 -2
- package/.claude/cfn-extras/skills/advanced-features/cfn-event-bus/SKILL.md +2 -2
- package/.claude/cfn-extras/skills/advanced-features/cfn-event-bus/config.json +1 -1
- package/package.json +1 -1
- package/.claude/core/agent-manager.js +0 -80
- package/.claude/core/agent-manager.js.map +0 -1
- package/.claude/core/config.js +0 -1241
- package/.claude/core/config.js.map +0 -1
- package/.claude/core/event-bus.js +0 -136
- package/.claude/core/event-bus.js.map +0 -1
- package/.claude/core/index.js +0 -6
- package/.claude/core/index.js.map +0 -1
- package/.claude/core/json-persistence.js +0 -112
- package/.claude/core/json-persistence.js.map +0 -1
- package/.claude/core/logger.js +0 -245
- package/.claude/core/logger.js.map +0 -1
- package/.claude/core/orchestrator-fixed.js +0 -236
- package/.claude/core/orchestrator-fixed.js.map +0 -1
- package/.claude/core/orchestrator.js +0 -1136
- package/.claude/core/orchestrator.js.map +0 -1
- package/.claude/core/persistence.js +0 -185
- package/.claude/core/persistence.js.map +0 -1
- package/.claude/core/project-manager.js +0 -80
- package/.claude/core/project-manager.js.map +0 -1
- package/.claude/core/slash-command.js +0 -24
- package/.claude/core/version.js +0 -35
- package/.claude/core/version.js.map +0 -1
- package/.claude/helpers/checkpoint-manager.sh +0 -251
- package/.claude/helpers/github-safe.js +0 -106
- package/.claude/helpers/github-setup.sh +0 -28
- package/.claude/helpers/quick-start.sh +0 -19
- package/.claude/helpers/setup-mcp.sh +0 -18
- package/.claude/helpers/standard-checkpoint-hooks.sh +0 -179
|
@@ -1,1136 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Main orchestrator for Claude-Flow
|
|
3
|
-
*/ import { SystemEvents } from '../utils/types.js';
|
|
4
|
-
import { SystemError, InitializationError, ShutdownError } from '../utils/errors.js';
|
|
5
|
-
import { delay, retry, circuitBreaker } from '../utils/helpers.js';
|
|
6
|
-
import { mkdir, writeFile, readFile } from 'fs/promises';
|
|
7
|
-
import { join, dirname } from 'path';
|
|
8
|
-
import { ClaudeAPIClient } from '../api/claude-client.js';
|
|
9
|
-
import { ConfigManager } from '../config/config-manager.js';
|
|
10
|
-
/**
|
|
11
|
-
* Session manager implementation with persistence
|
|
12
|
-
*/ let SessionManager = class SessionManager {
|
|
13
|
-
terminalManager;
|
|
14
|
-
memoryManager;
|
|
15
|
-
eventBus;
|
|
16
|
-
logger;
|
|
17
|
-
config;
|
|
18
|
-
sessions = new Map();
|
|
19
|
-
sessionProfiles = new Map();
|
|
20
|
-
persistencePath;
|
|
21
|
-
persistenceCircuitBreaker;
|
|
22
|
-
constructor(terminalManager, memoryManager, eventBus, logger, config){
|
|
23
|
-
this.terminalManager = terminalManager;
|
|
24
|
-
this.memoryManager = memoryManager;
|
|
25
|
-
this.eventBus = eventBus;
|
|
26
|
-
this.logger = logger;
|
|
27
|
-
this.config = config;
|
|
28
|
-
this.persistencePath = join(config.orchestrator.dataDir || './data', 'sessions.json');
|
|
29
|
-
// Circuit breaker for persistence operations
|
|
30
|
-
this.persistenceCircuitBreaker = circuitBreaker('SessionPersistence', {
|
|
31
|
-
threshold: 5,
|
|
32
|
-
timeout: 30000,
|
|
33
|
-
resetTimeout: 60000
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
async createSession(profile) {
|
|
37
|
-
try {
|
|
38
|
-
// Create terminal with retry logic
|
|
39
|
-
const terminalId = await retry(()=>this.terminalManager.spawnTerminal(profile), {
|
|
40
|
-
maxAttempts: 3,
|
|
41
|
-
initialDelay: 1000
|
|
42
|
-
});
|
|
43
|
-
// Create memory bank with retry logic
|
|
44
|
-
const memoryBankId = await retry(()=>this.memoryManager.createBank(profile.id), {
|
|
45
|
-
maxAttempts: 3,
|
|
46
|
-
initialDelay: 1000
|
|
47
|
-
});
|
|
48
|
-
// Create session
|
|
49
|
-
const session = {
|
|
50
|
-
id: `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
51
|
-
agentId: profile.id,
|
|
52
|
-
terminalId,
|
|
53
|
-
startTime: new Date(),
|
|
54
|
-
status: 'active',
|
|
55
|
-
lastActivity: new Date(),
|
|
56
|
-
memoryBankId
|
|
57
|
-
};
|
|
58
|
-
this.sessions.set(session.id, session);
|
|
59
|
-
this.sessionProfiles.set(session.id, profile);
|
|
60
|
-
this.logger.info('Session created', {
|
|
61
|
-
sessionId: session.id,
|
|
62
|
-
agentId: profile.id,
|
|
63
|
-
terminalId,
|
|
64
|
-
memoryBankId
|
|
65
|
-
});
|
|
66
|
-
// Persist sessions asynchronously
|
|
67
|
-
this.persistSessions().catch((error)=>this.logger.error('Failed to persist sessions', error));
|
|
68
|
-
return session;
|
|
69
|
-
} catch (error) {
|
|
70
|
-
this.logger.error('Failed to create session', {
|
|
71
|
-
agentId: profile.id,
|
|
72
|
-
error
|
|
73
|
-
});
|
|
74
|
-
throw new SystemError(`Failed to create session for agent ${profile.id}`, {
|
|
75
|
-
error
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
getSession(sessionId) {
|
|
80
|
-
return this.sessions.get(sessionId);
|
|
81
|
-
}
|
|
82
|
-
getActiveSessions() {
|
|
83
|
-
return Array.from(this.sessions.values()).filter((session)=>session.status === 'active' || session.status === 'idle');
|
|
84
|
-
}
|
|
85
|
-
async terminateSession(sessionId) {
|
|
86
|
-
const session = this.sessions.get(sessionId);
|
|
87
|
-
if (!session) {
|
|
88
|
-
throw new Error(`Session not found: ${sessionId}`);
|
|
89
|
-
}
|
|
90
|
-
try {
|
|
91
|
-
// Update session status first
|
|
92
|
-
session.status = 'terminated';
|
|
93
|
-
session.endTime = new Date();
|
|
94
|
-
// Terminate terminal with timeout
|
|
95
|
-
await Promise.race([
|
|
96
|
-
this.terminalManager.terminateTerminal(session.terminalId),
|
|
97
|
-
delay(5000).then(()=>{
|
|
98
|
-
throw new Error('Terminal termination timeout');
|
|
99
|
-
})
|
|
100
|
-
]).catch((error)=>{
|
|
101
|
-
this.logger.error('Error terminating terminal', {
|
|
102
|
-
sessionId,
|
|
103
|
-
error
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
// Close memory bank with timeout
|
|
107
|
-
await Promise.race([
|
|
108
|
-
this.memoryManager.closeBank(session.memoryBankId),
|
|
109
|
-
delay(5000).then(()=>{
|
|
110
|
-
throw new Error('Memory bank close timeout');
|
|
111
|
-
})
|
|
112
|
-
]).catch((error)=>{
|
|
113
|
-
this.logger.error('Error closing memory bank', {
|
|
114
|
-
sessionId,
|
|
115
|
-
error
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
// Clean up
|
|
119
|
-
this.sessionProfiles.delete(sessionId);
|
|
120
|
-
this.logger.info('Session terminated', {
|
|
121
|
-
sessionId,
|
|
122
|
-
duration: session.endTime.getTime() - session.startTime.getTime()
|
|
123
|
-
});
|
|
124
|
-
// Persist sessions asynchronously
|
|
125
|
-
this.persistSessions().catch((error)=>this.logger.error('Failed to persist sessions', error));
|
|
126
|
-
} catch (error) {
|
|
127
|
-
this.logger.error('Error during session termination', {
|
|
128
|
-
sessionId,
|
|
129
|
-
error
|
|
130
|
-
});
|
|
131
|
-
throw error;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
async terminateAllSessions() {
|
|
135
|
-
const sessions = this.getActiveSessions();
|
|
136
|
-
// Terminate sessions in batches to avoid overwhelming the system
|
|
137
|
-
const batchSize = 5;
|
|
138
|
-
for(let i = 0; i < sessions.length; i += batchSize){
|
|
139
|
-
const batch = sessions.slice(i, i + batchSize);
|
|
140
|
-
await Promise.allSettled(batch.map((session)=>this.terminateSession(session.id)));
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
removeSession(sessionId) {
|
|
144
|
-
this.sessions.delete(sessionId);
|
|
145
|
-
this.sessionProfiles.delete(sessionId);
|
|
146
|
-
}
|
|
147
|
-
async persistSessions() {
|
|
148
|
-
if (!this.config.orchestrator.persistSessions) {
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
try {
|
|
152
|
-
await this.persistenceCircuitBreaker.execute(async ()=>{
|
|
153
|
-
const data = {
|
|
154
|
-
sessions: Array.from(this.sessions.values()).map((session)=>({
|
|
155
|
-
...session,
|
|
156
|
-
profile: this.sessionProfiles.get(session.id)
|
|
157
|
-
})).filter((s)=>s.profile),
|
|
158
|
-
taskQueue: [],
|
|
159
|
-
metrics: {
|
|
160
|
-
completedTasks: 0,
|
|
161
|
-
failedTasks: 0,
|
|
162
|
-
totalTaskDuration: 0
|
|
163
|
-
},
|
|
164
|
-
savedAt: new Date()
|
|
165
|
-
};
|
|
166
|
-
await mkdir(dirname(this.persistencePath), {
|
|
167
|
-
recursive: true
|
|
168
|
-
});
|
|
169
|
-
await writeFile(this.persistencePath, JSON.stringify(data, null, 2), 'utf8');
|
|
170
|
-
this.logger.debug('Sessions persisted', {
|
|
171
|
-
count: data.sessions.length
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
} catch (error) {
|
|
175
|
-
this.logger.error('Failed to persist sessions', error);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
async restoreSessions() {
|
|
179
|
-
if (!this.config.orchestrator.persistSessions) {
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
try {
|
|
183
|
-
const data = await readFile(this.persistencePath, 'utf8');
|
|
184
|
-
const persistence = JSON.parse(data);
|
|
185
|
-
// Restore only active/idle sessions
|
|
186
|
-
const sessionsToRestore = persistence.sessions.filter((s)=>s.status === 'active' || s.status === 'idle');
|
|
187
|
-
for (const sessionData of sessionsToRestore){
|
|
188
|
-
try {
|
|
189
|
-
// Recreate session
|
|
190
|
-
const session = await this.createSession(sessionData.profile);
|
|
191
|
-
// Update with persisted data
|
|
192
|
-
Object.assign(session, {
|
|
193
|
-
id: sessionData.id,
|
|
194
|
-
startTime: new Date(sessionData.startTime),
|
|
195
|
-
lastActivity: new Date(sessionData.lastActivity)
|
|
196
|
-
});
|
|
197
|
-
this.logger.info('Session restored', {
|
|
198
|
-
sessionId: session.id
|
|
199
|
-
});
|
|
200
|
-
} catch (error) {
|
|
201
|
-
this.logger.error('Failed to restore session', {
|
|
202
|
-
sessionId: sessionData.id,
|
|
203
|
-
error
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
} catch (error) {
|
|
208
|
-
if (error.code !== 'ENOENT') {
|
|
209
|
-
this.logger.error('Failed to restore sessions', error);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
/**
|
|
215
|
-
* Main orchestrator implementation with enhanced features
|
|
216
|
-
*/ export class Orchestrator {
|
|
217
|
-
config;
|
|
218
|
-
terminalManager;
|
|
219
|
-
memoryManager;
|
|
220
|
-
coordinationManager;
|
|
221
|
-
mcpServer;
|
|
222
|
-
eventBus;
|
|
223
|
-
logger;
|
|
224
|
-
initialized = false;
|
|
225
|
-
shutdownInProgress = false;
|
|
226
|
-
sessionManager;
|
|
227
|
-
healthCheckInterval;
|
|
228
|
-
maintenanceInterval;
|
|
229
|
-
metricsInterval;
|
|
230
|
-
agents = new Map();
|
|
231
|
-
taskQueue = [];
|
|
232
|
-
taskHistory = new Map();
|
|
233
|
-
startTime = Date.now();
|
|
234
|
-
claudeClient;
|
|
235
|
-
configManager;
|
|
236
|
-
// Metrics tracking
|
|
237
|
-
metrics = {
|
|
238
|
-
completedTasks: 0,
|
|
239
|
-
failedTasks: 0,
|
|
240
|
-
totalTaskDuration: 0
|
|
241
|
-
};
|
|
242
|
-
// Circuit breakers for critical operations
|
|
243
|
-
healthCheckCircuitBreaker;
|
|
244
|
-
taskAssignmentCircuitBreaker;
|
|
245
|
-
constructor(config, terminalManager, memoryManager, coordinationManager, mcpServer, eventBus, logger){
|
|
246
|
-
this.config = config;
|
|
247
|
-
this.terminalManager = terminalManager;
|
|
248
|
-
this.memoryManager = memoryManager;
|
|
249
|
-
this.coordinationManager = coordinationManager;
|
|
250
|
-
this.mcpServer = mcpServer;
|
|
251
|
-
this.eventBus = eventBus;
|
|
252
|
-
this.logger = logger;
|
|
253
|
-
this.sessionManager = new SessionManager(terminalManager, memoryManager, eventBus, logger, config);
|
|
254
|
-
this.configManager = ConfigManager.getInstance();
|
|
255
|
-
// Initialize circuit breakers
|
|
256
|
-
this.healthCheckCircuitBreaker = circuitBreaker('HealthCheck', {
|
|
257
|
-
threshold: 3,
|
|
258
|
-
timeout: 10000,
|
|
259
|
-
resetTimeout: 30000
|
|
260
|
-
});
|
|
261
|
-
this.taskAssignmentCircuitBreaker = circuitBreaker('TaskAssignment', {
|
|
262
|
-
threshold: 5,
|
|
263
|
-
timeout: 5000,
|
|
264
|
-
resetTimeout: 20000
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
async initialize() {
|
|
268
|
-
if (this.initialized) {
|
|
269
|
-
throw new InitializationError('Orchestrator already initialized');
|
|
270
|
-
}
|
|
271
|
-
this.logger.info('Initializing orchestrator...');
|
|
272
|
-
const startTime = Date.now();
|
|
273
|
-
try {
|
|
274
|
-
// Initialize components in parallel where possible
|
|
275
|
-
await Promise.all([
|
|
276
|
-
this.initializeComponent('Terminal Manager', ()=>this.terminalManager.initialize()),
|
|
277
|
-
this.initializeComponent('Memory Manager', ()=>this.memoryManager.initialize()),
|
|
278
|
-
this.initializeComponent('Coordination Manager', ()=>this.coordinationManager.initialize())
|
|
279
|
-
]);
|
|
280
|
-
// MCP server needs to be started after other components
|
|
281
|
-
await this.initializeComponent('MCP Server', ()=>this.mcpServer.start());
|
|
282
|
-
// Initialize Claude API client if configured
|
|
283
|
-
if (this.configManager.isClaudeAPIConfigured()) {
|
|
284
|
-
try {
|
|
285
|
-
this.claudeClient = new ClaudeAPIClient(this.logger, this.configManager);
|
|
286
|
-
this.logger.info('Claude API client initialized', {
|
|
287
|
-
model: this.claudeClient.getConfig().model,
|
|
288
|
-
temperature: this.claudeClient.getConfig().temperature
|
|
289
|
-
});
|
|
290
|
-
} catch (error) {
|
|
291
|
-
this.logger.warn('Failed to initialize Claude API client', error);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
// Restore persisted sessions
|
|
295
|
-
await this.sessionManager.restoreSessions();
|
|
296
|
-
// Set up event handlers
|
|
297
|
-
this.setupEventHandlers();
|
|
298
|
-
// Start background tasks
|
|
299
|
-
this.startHealthChecks();
|
|
300
|
-
this.startMaintenanceTasks();
|
|
301
|
-
this.startMetricsCollection();
|
|
302
|
-
this.initialized = true;
|
|
303
|
-
const initDuration = Date.now() - startTime;
|
|
304
|
-
this.eventBus.emit(SystemEvents.SYSTEM_READY, {
|
|
305
|
-
timestamp: new Date()
|
|
306
|
-
});
|
|
307
|
-
this.logger.info('Orchestrator initialized successfully', {
|
|
308
|
-
duration: initDuration
|
|
309
|
-
});
|
|
310
|
-
} catch (error) {
|
|
311
|
-
this.logger.error('Failed to initialize orchestrator', error);
|
|
312
|
-
// Attempt cleanup on initialization failure
|
|
313
|
-
await this.emergencyShutdown();
|
|
314
|
-
throw new InitializationError('Orchestrator', {
|
|
315
|
-
error
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
async shutdown() {
|
|
320
|
-
if (!this.initialized || this.shutdownInProgress) {
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
this.shutdownInProgress = true;
|
|
324
|
-
this.logger.info('Shutting down orchestrator...');
|
|
325
|
-
const shutdownStart = Date.now();
|
|
326
|
-
try {
|
|
327
|
-
// Stop background tasks
|
|
328
|
-
this.stopBackgroundTasks();
|
|
329
|
-
// Save current state
|
|
330
|
-
await this.sessionManager.persistSessions();
|
|
331
|
-
// Process any remaining critical tasks
|
|
332
|
-
await this.processShutdownTasks();
|
|
333
|
-
// Terminate all sessions
|
|
334
|
-
await this.sessionManager.terminateAllSessions();
|
|
335
|
-
// Shutdown components with timeout
|
|
336
|
-
await Promise.race([
|
|
337
|
-
this.shutdownComponents(),
|
|
338
|
-
delay(this.config.orchestrator.shutdownTimeout)
|
|
339
|
-
]);
|
|
340
|
-
const shutdownDuration = Date.now() - shutdownStart;
|
|
341
|
-
this.eventBus.emit(SystemEvents.SYSTEM_SHUTDOWN, {
|
|
342
|
-
reason: 'Graceful shutdown'
|
|
343
|
-
});
|
|
344
|
-
this.logger.info('Orchestrator shutdown complete', {
|
|
345
|
-
duration: shutdownDuration
|
|
346
|
-
});
|
|
347
|
-
} catch (error) {
|
|
348
|
-
this.logger.error('Error during shutdown', error);
|
|
349
|
-
// Force shutdown if graceful shutdown fails
|
|
350
|
-
await this.emergencyShutdown();
|
|
351
|
-
throw new ShutdownError('Failed to shutdown gracefully', {
|
|
352
|
-
error
|
|
353
|
-
});
|
|
354
|
-
} finally{
|
|
355
|
-
this.initialized = false;
|
|
356
|
-
this.shutdownInProgress = false;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
async spawnAgent(profile) {
|
|
360
|
-
if (!this.initialized) {
|
|
361
|
-
throw new SystemError('Orchestrator not initialized');
|
|
362
|
-
}
|
|
363
|
-
// Check agent limit
|
|
364
|
-
if (this.agents.size >= this.config.orchestrator.maxConcurrentAgents) {
|
|
365
|
-
throw new SystemError('Maximum concurrent agents reached');
|
|
366
|
-
}
|
|
367
|
-
// Validate agent profile
|
|
368
|
-
this.validateAgentProfile(profile);
|
|
369
|
-
this.logger.info('Spawning agent', {
|
|
370
|
-
agentId: profile.id,
|
|
371
|
-
type: profile.type
|
|
372
|
-
});
|
|
373
|
-
try {
|
|
374
|
-
// Create session with retry
|
|
375
|
-
const session = await retry(()=>this.sessionManager.createSession(profile), {
|
|
376
|
-
maxAttempts: 3,
|
|
377
|
-
initialDelay: 2000
|
|
378
|
-
});
|
|
379
|
-
// Store agent profile
|
|
380
|
-
this.agents.set(profile.id, profile);
|
|
381
|
-
// Emit event
|
|
382
|
-
this.eventBus.emit(SystemEvents.AGENT_SPAWNED, {
|
|
383
|
-
agentId: profile.id,
|
|
384
|
-
profile,
|
|
385
|
-
sessionId: session.id
|
|
386
|
-
});
|
|
387
|
-
// Start agent health monitoring
|
|
388
|
-
this.startAgentHealthMonitoring(profile.id);
|
|
389
|
-
return session.id;
|
|
390
|
-
} catch (error) {
|
|
391
|
-
this.logger.error('Failed to spawn agent', {
|
|
392
|
-
agentId: profile.id,
|
|
393
|
-
error
|
|
394
|
-
});
|
|
395
|
-
throw error;
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
async terminateAgent(agentId) {
|
|
399
|
-
if (!this.initialized) {
|
|
400
|
-
throw new SystemError('Orchestrator not initialized');
|
|
401
|
-
}
|
|
402
|
-
const profile = this.agents.get(agentId);
|
|
403
|
-
if (!profile) {
|
|
404
|
-
throw new SystemError(`Agent not found: ${agentId}`);
|
|
405
|
-
}
|
|
406
|
-
this.logger.info('Terminating agent', {
|
|
407
|
-
agentId
|
|
408
|
-
});
|
|
409
|
-
try {
|
|
410
|
-
// Cancel any assigned tasks
|
|
411
|
-
await this.cancelAgentTasks(agentId);
|
|
412
|
-
// Find and terminate all sessions for this agent
|
|
413
|
-
const sessions = this.sessionManager.getActiveSessions().filter((session)=>session.agentId === agentId);
|
|
414
|
-
await Promise.allSettled(sessions.map((session)=>this.sessionManager.terminateSession(session.id)));
|
|
415
|
-
// Remove agent
|
|
416
|
-
this.agents.delete(agentId);
|
|
417
|
-
// Emit event
|
|
418
|
-
this.eventBus.emit(SystemEvents.AGENT_TERMINATED, {
|
|
419
|
-
agentId,
|
|
420
|
-
reason: 'User requested'
|
|
421
|
-
});
|
|
422
|
-
} catch (error) {
|
|
423
|
-
this.logger.error('Failed to terminate agent', {
|
|
424
|
-
agentId,
|
|
425
|
-
error
|
|
426
|
-
});
|
|
427
|
-
throw error;
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
async assignTask(task) {
|
|
431
|
-
if (!this.initialized) {
|
|
432
|
-
throw new SystemError('Orchestrator not initialized');
|
|
433
|
-
}
|
|
434
|
-
// Validate task
|
|
435
|
-
this.validateTask(task);
|
|
436
|
-
// Store task in history
|
|
437
|
-
this.taskHistory.set(task.id, task);
|
|
438
|
-
try {
|
|
439
|
-
await this.taskAssignmentCircuitBreaker.execute(async ()=>{
|
|
440
|
-
// Add to queue if no agent assigned
|
|
441
|
-
if (!task.assignedAgent) {
|
|
442
|
-
if (this.taskQueue.length >= this.config.orchestrator.taskQueueSize) {
|
|
443
|
-
throw new SystemError('Task queue is full');
|
|
444
|
-
}
|
|
445
|
-
this.taskQueue.push(task);
|
|
446
|
-
this.eventBus.emit(SystemEvents.TASK_CREATED, {
|
|
447
|
-
task
|
|
448
|
-
});
|
|
449
|
-
// Try to assign immediately
|
|
450
|
-
await this.processTaskQueue();
|
|
451
|
-
return;
|
|
452
|
-
}
|
|
453
|
-
// Assign to specific agent
|
|
454
|
-
const agent = this.agents.get(task.assignedAgent);
|
|
455
|
-
if (!agent) {
|
|
456
|
-
throw new SystemError(`Agent not found: ${task.assignedAgent}`);
|
|
457
|
-
}
|
|
458
|
-
await this.coordinationManager.assignTask(task, task.assignedAgent);
|
|
459
|
-
this.eventBus.emit(SystemEvents.TASK_ASSIGNED, {
|
|
460
|
-
taskId: task.id,
|
|
461
|
-
agentId: task.assignedAgent
|
|
462
|
-
});
|
|
463
|
-
});
|
|
464
|
-
} catch (error) {
|
|
465
|
-
this.logger.error('Failed to assign task', {
|
|
466
|
-
taskId: task.id,
|
|
467
|
-
error
|
|
468
|
-
});
|
|
469
|
-
throw error;
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
async getHealthStatus() {
|
|
473
|
-
try {
|
|
474
|
-
return await this.healthCheckCircuitBreaker.execute(async ()=>{
|
|
475
|
-
const components = {};
|
|
476
|
-
// Check all components in parallel
|
|
477
|
-
const [terminal, memory, coordination, mcp] = await Promise.allSettled([
|
|
478
|
-
this.getComponentHealth('Terminal Manager', async ()=>await this.terminalManager.getHealthStatus()),
|
|
479
|
-
this.getComponentHealth('Memory Manager', async ()=>await this.memoryManager.getHealthStatus()),
|
|
480
|
-
this.getComponentHealth('Coordination Manager', async ()=>await this.coordinationManager.getHealthStatus()),
|
|
481
|
-
this.getComponentHealth('MCP Server', async ()=>await this.mcpServer.getHealthStatus())
|
|
482
|
-
]);
|
|
483
|
-
// Process results
|
|
484
|
-
components.terminal = this.processHealthResult(terminal, 'Terminal Manager');
|
|
485
|
-
components.memory = this.processHealthResult(memory, 'Memory Manager');
|
|
486
|
-
components.coordination = this.processHealthResult(coordination, 'Coordination Manager');
|
|
487
|
-
components.mcp = this.processHealthResult(mcp, 'MCP Server');
|
|
488
|
-
// Add orchestrator self-check
|
|
489
|
-
components.orchestrator = {
|
|
490
|
-
name: 'Orchestrator',
|
|
491
|
-
status: 'healthy',
|
|
492
|
-
lastCheck: new Date(),
|
|
493
|
-
metrics: {
|
|
494
|
-
uptime: Date.now() - this.startTime,
|
|
495
|
-
activeAgents: this.agents.size,
|
|
496
|
-
queuedTasks: this.taskQueue.length,
|
|
497
|
-
memoryUsage: process.memoryUsage().heapUsed / 1024 / 1024
|
|
498
|
-
}
|
|
499
|
-
};
|
|
500
|
-
// Determine overall status
|
|
501
|
-
const statuses = Object.values(components).map((c)=>c.status);
|
|
502
|
-
let overallStatus = 'healthy';
|
|
503
|
-
if (statuses.some((s)=>s === 'unhealthy')) {
|
|
504
|
-
overallStatus = 'unhealthy';
|
|
505
|
-
} else if (statuses.some((s)=>s === 'degraded')) {
|
|
506
|
-
overallStatus = 'degraded';
|
|
507
|
-
}
|
|
508
|
-
return {
|
|
509
|
-
status: overallStatus,
|
|
510
|
-
components,
|
|
511
|
-
timestamp: new Date()
|
|
512
|
-
};
|
|
513
|
-
});
|
|
514
|
-
} catch (error) {
|
|
515
|
-
this.logger.error('Health check failed', error);
|
|
516
|
-
// Return degraded status if health check fails
|
|
517
|
-
return {
|
|
518
|
-
status: 'degraded',
|
|
519
|
-
components: {
|
|
520
|
-
orchestrator: {
|
|
521
|
-
name: 'Orchestrator',
|
|
522
|
-
status: 'degraded',
|
|
523
|
-
lastCheck: new Date(),
|
|
524
|
-
error: 'Health check circuit breaker open'
|
|
525
|
-
}
|
|
526
|
-
},
|
|
527
|
-
timestamp: new Date()
|
|
528
|
-
};
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
async getMetrics() {
|
|
532
|
-
const memUsage = process.memoryUsage();
|
|
533
|
-
const cpuUsage = process.cpuUsage();
|
|
534
|
-
const avgTaskDuration = this.metrics.completedTasks > 0 ? this.metrics.totalTaskDuration / this.metrics.completedTasks : 0;
|
|
535
|
-
return {
|
|
536
|
-
uptime: Date.now() - this.startTime,
|
|
537
|
-
totalAgents: this.agents.size,
|
|
538
|
-
activeAgents: this.sessionManager.getActiveSessions().length,
|
|
539
|
-
totalTasks: this.taskHistory.size,
|
|
540
|
-
completedTasks: this.metrics.completedTasks,
|
|
541
|
-
failedTasks: this.metrics.failedTasks,
|
|
542
|
-
queuedTasks: this.taskQueue.length,
|
|
543
|
-
avgTaskDuration,
|
|
544
|
-
memoryUsage: memUsage,
|
|
545
|
-
cpuUsage: cpuUsage,
|
|
546
|
-
timestamp: new Date()
|
|
547
|
-
};
|
|
548
|
-
}
|
|
549
|
-
async performMaintenance() {
|
|
550
|
-
this.logger.debug('Performing maintenance tasks');
|
|
551
|
-
try {
|
|
552
|
-
// Clean up terminated sessions
|
|
553
|
-
await this.cleanupTerminatedSessions();
|
|
554
|
-
// Clean up old task history
|
|
555
|
-
await this.cleanupTaskHistory();
|
|
556
|
-
// Perform component maintenance
|
|
557
|
-
await Promise.allSettled([
|
|
558
|
-
this.terminalManager.performMaintenance(),
|
|
559
|
-
this.memoryManager.performMaintenance(),
|
|
560
|
-
this.coordinationManager.performMaintenance()
|
|
561
|
-
]);
|
|
562
|
-
// Persist current state
|
|
563
|
-
await this.sessionManager.persistSessions();
|
|
564
|
-
// Force garbage collection if available
|
|
565
|
-
if (global.gc) {
|
|
566
|
-
global.gc();
|
|
567
|
-
}
|
|
568
|
-
this.logger.debug('Maintenance tasks completed');
|
|
569
|
-
} catch (error) {
|
|
570
|
-
this.logger.error('Error during maintenance', error);
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
setupEventHandlers() {
|
|
574
|
-
// Handle task lifecycle events
|
|
575
|
-
this.eventBus.on(SystemEvents.TASK_STARTED, (data)=>{
|
|
576
|
-
const { taskId, agentId } = data;
|
|
577
|
-
const task = this.taskHistory.get(taskId);
|
|
578
|
-
if (task) {
|
|
579
|
-
task.status = 'running';
|
|
580
|
-
task.startedAt = new Date();
|
|
581
|
-
}
|
|
582
|
-
});
|
|
583
|
-
this.eventBus.on(SystemEvents.TASK_COMPLETED, async (data)=>{
|
|
584
|
-
const { taskId, result } = data;
|
|
585
|
-
const task = this.taskHistory.get(taskId);
|
|
586
|
-
if (task) {
|
|
587
|
-
task.status = 'completed';
|
|
588
|
-
task.completedAt = new Date();
|
|
589
|
-
if (result !== undefined) {
|
|
590
|
-
task.output = result;
|
|
591
|
-
}
|
|
592
|
-
// Update metrics
|
|
593
|
-
this.metrics.completedTasks++;
|
|
594
|
-
if (task.startedAt) {
|
|
595
|
-
this.metrics.totalTaskDuration += task.completedAt.getTime() - task.startedAt.getTime();
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
await this.processTaskQueue();
|
|
599
|
-
});
|
|
600
|
-
this.eventBus.on(SystemEvents.TASK_FAILED, async (data)=>{
|
|
601
|
-
const { taskId, error } = data;
|
|
602
|
-
const task = this.taskHistory.get(taskId);
|
|
603
|
-
if (task) {
|
|
604
|
-
task.status = 'failed';
|
|
605
|
-
task.completedAt = new Date();
|
|
606
|
-
task.error = error;
|
|
607
|
-
// Update metrics
|
|
608
|
-
this.metrics.failedTasks++;
|
|
609
|
-
}
|
|
610
|
-
// Retry or requeue based on configuration
|
|
611
|
-
await this.handleTaskFailure(taskId, error);
|
|
612
|
-
});
|
|
613
|
-
// Handle agent events
|
|
614
|
-
this.eventBus.on(SystemEvents.AGENT_ERROR, async (data)=>{
|
|
615
|
-
const { agentId, error } = data;
|
|
616
|
-
this.logger.error('Agent error', {
|
|
617
|
-
agentId,
|
|
618
|
-
error
|
|
619
|
-
});
|
|
620
|
-
// Implement agent recovery
|
|
621
|
-
await this.handleAgentError(agentId, error);
|
|
622
|
-
});
|
|
623
|
-
this.eventBus.on(SystemEvents.AGENT_IDLE, async (data)=>{
|
|
624
|
-
const { agentId } = data;
|
|
625
|
-
// Update session status
|
|
626
|
-
const sessions = this.sessionManager.getActiveSessions().filter((s)=>s.agentId === agentId);
|
|
627
|
-
sessions.forEach((s)=>s.status = 'idle');
|
|
628
|
-
// Try to assign queued tasks
|
|
629
|
-
await this.processTaskQueue();
|
|
630
|
-
});
|
|
631
|
-
// Handle system events
|
|
632
|
-
this.eventBus.on(SystemEvents.SYSTEM_ERROR, (data)=>{
|
|
633
|
-
const { error, component } = data;
|
|
634
|
-
this.logger.error('System error', {
|
|
635
|
-
component,
|
|
636
|
-
error
|
|
637
|
-
});
|
|
638
|
-
// Implement system-level error recovery
|
|
639
|
-
this.handleSystemError(component, error);
|
|
640
|
-
});
|
|
641
|
-
// Handle resource events
|
|
642
|
-
this.eventBus.on(SystemEvents.DEADLOCK_DETECTED, (data)=>{
|
|
643
|
-
const { agents, resources } = data;
|
|
644
|
-
this.logger.error('Deadlock detected', {
|
|
645
|
-
agents,
|
|
646
|
-
resources
|
|
647
|
-
});
|
|
648
|
-
// Implement deadlock resolution
|
|
649
|
-
this.resolveDeadlock(agents, resources);
|
|
650
|
-
});
|
|
651
|
-
}
|
|
652
|
-
startHealthChecks() {
|
|
653
|
-
this.healthCheckInterval = setInterval(async ()=>{
|
|
654
|
-
try {
|
|
655
|
-
const health = await this.getHealthStatus();
|
|
656
|
-
this.eventBus.emit(SystemEvents.SYSTEM_HEALTHCHECK, {
|
|
657
|
-
status: health
|
|
658
|
-
});
|
|
659
|
-
if (health.status === 'unhealthy') {
|
|
660
|
-
this.logger.warn('System health check failed', health);
|
|
661
|
-
// Attempt recovery for unhealthy components
|
|
662
|
-
await this.recoverUnhealthyComponents(health);
|
|
663
|
-
}
|
|
664
|
-
} catch (error) {
|
|
665
|
-
this.logger.error('Health check error', error);
|
|
666
|
-
}
|
|
667
|
-
}, this.config.orchestrator.healthCheckInterval);
|
|
668
|
-
}
|
|
669
|
-
startMaintenanceTasks() {
|
|
670
|
-
this.maintenanceInterval = setInterval(async ()=>{
|
|
671
|
-
await this.performMaintenance();
|
|
672
|
-
}, this.config.orchestrator.maintenanceInterval || 300000); // 5 minutes default
|
|
673
|
-
}
|
|
674
|
-
startMetricsCollection() {
|
|
675
|
-
this.metricsInterval = setInterval(async ()=>{
|
|
676
|
-
try {
|
|
677
|
-
const metrics = await this.getMetrics();
|
|
678
|
-
this.logger.debug('Metrics collected', metrics);
|
|
679
|
-
// Emit metrics event for monitoring systems
|
|
680
|
-
this.eventBus.emit('metrics:collected', metrics);
|
|
681
|
-
} catch (error) {
|
|
682
|
-
this.logger.error('Metrics collection error', error);
|
|
683
|
-
}
|
|
684
|
-
}, this.config.orchestrator.metricsInterval || 60000); // 1 minute default
|
|
685
|
-
}
|
|
686
|
-
stopBackgroundTasks() {
|
|
687
|
-
if (this.healthCheckInterval) {
|
|
688
|
-
clearInterval(this.healthCheckInterval);
|
|
689
|
-
}
|
|
690
|
-
if (this.maintenanceInterval) {
|
|
691
|
-
clearInterval(this.maintenanceInterval);
|
|
692
|
-
}
|
|
693
|
-
if (this.metricsInterval) {
|
|
694
|
-
clearInterval(this.metricsInterval);
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
async shutdownComponents() {
|
|
698
|
-
const shutdownTasks = [
|
|
699
|
-
this.shutdownComponent('Terminal Manager', ()=>this.terminalManager.shutdown()),
|
|
700
|
-
this.shutdownComponent('Memory Manager', ()=>this.memoryManager.shutdown()),
|
|
701
|
-
this.shutdownComponent('Coordination Manager', ()=>this.coordinationManager.shutdown()),
|
|
702
|
-
this.shutdownComponent('MCP Server', ()=>this.mcpServer.stop())
|
|
703
|
-
];
|
|
704
|
-
const results = await Promise.allSettled(shutdownTasks);
|
|
705
|
-
// Log any shutdown failures
|
|
706
|
-
results.forEach((result, index)=>{
|
|
707
|
-
if (result.status === 'rejected') {
|
|
708
|
-
const componentName = [
|
|
709
|
-
'Terminal Manager',
|
|
710
|
-
'Memory Manager',
|
|
711
|
-
'Coordination Manager',
|
|
712
|
-
'MCP Server'
|
|
713
|
-
][index];
|
|
714
|
-
this.logger.error(`Failed to shutdown ${componentName}`, result.reason);
|
|
715
|
-
}
|
|
716
|
-
});
|
|
717
|
-
}
|
|
718
|
-
async emergencyShutdown() {
|
|
719
|
-
this.logger.warn('Performing emergency shutdown');
|
|
720
|
-
try {
|
|
721
|
-
// Force stop all components
|
|
722
|
-
await Promise.allSettled([
|
|
723
|
-
this.terminalManager.shutdown().catch(()=>{}),
|
|
724
|
-
this.memoryManager.shutdown().catch(()=>{}),
|
|
725
|
-
this.coordinationManager.shutdown().catch(()=>{}),
|
|
726
|
-
this.mcpServer.stop().catch(()=>{})
|
|
727
|
-
]);
|
|
728
|
-
} catch (error) {
|
|
729
|
-
this.logger.error('Emergency shutdown error', error);
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
async processTaskQueue() {
|
|
733
|
-
if (this.taskQueue.length === 0) {
|
|
734
|
-
return;
|
|
735
|
-
}
|
|
736
|
-
const availableAgents = await this.getAvailableAgents();
|
|
737
|
-
while(this.taskQueue.length > 0 && availableAgents.length > 0){
|
|
738
|
-
const task = this.taskQueue.shift();
|
|
739
|
-
const agent = this.selectAgentForTask(task, availableAgents);
|
|
740
|
-
if (agent) {
|
|
741
|
-
task.assignedAgent = agent.id;
|
|
742
|
-
task.status = 'assigned';
|
|
743
|
-
try {
|
|
744
|
-
await this.coordinationManager.assignTask(task, agent.id);
|
|
745
|
-
this.eventBus.emit(SystemEvents.TASK_ASSIGNED, {
|
|
746
|
-
taskId: task.id,
|
|
747
|
-
agentId: agent.id
|
|
748
|
-
});
|
|
749
|
-
// Remove agent from available list
|
|
750
|
-
const index = availableAgents.indexOf(agent);
|
|
751
|
-
availableAgents.splice(index, 1);
|
|
752
|
-
} catch (error) {
|
|
753
|
-
// Put task back in queue
|
|
754
|
-
this.taskQueue.unshift(task);
|
|
755
|
-
this.logger.error('Failed to assign task', {
|
|
756
|
-
taskId: task.id,
|
|
757
|
-
error
|
|
758
|
-
});
|
|
759
|
-
break;
|
|
760
|
-
}
|
|
761
|
-
} else {
|
|
762
|
-
// No suitable agent, put task back
|
|
763
|
-
this.taskQueue.unshift(task);
|
|
764
|
-
break;
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
async getAvailableAgents() {
|
|
769
|
-
const sessions = this.sessionManager.getActiveSessions();
|
|
770
|
-
const available = [];
|
|
771
|
-
for (const session of sessions){
|
|
772
|
-
if (session.status === 'idle' || session.status === 'active') {
|
|
773
|
-
const profile = this.agents.get(session.agentId);
|
|
774
|
-
if (profile) {
|
|
775
|
-
try {
|
|
776
|
-
const taskCount = await this.coordinationManager.getAgentTaskCount(profile.id);
|
|
777
|
-
if (taskCount < profile.maxConcurrentTasks) {
|
|
778
|
-
available.push(profile);
|
|
779
|
-
}
|
|
780
|
-
} catch (error) {
|
|
781
|
-
this.logger.error('Failed to get agent task count', {
|
|
782
|
-
agentId: profile.id,
|
|
783
|
-
error
|
|
784
|
-
});
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
return available.sort((a, b)=>b.priority - a.priority);
|
|
790
|
-
}
|
|
791
|
-
selectAgentForTask(task, agents) {
|
|
792
|
-
// Score agents based on capabilities, load, and priority
|
|
793
|
-
const scoredAgents = agents.map((agent)=>{
|
|
794
|
-
let score = agent.priority * 10;
|
|
795
|
-
// Check capability match
|
|
796
|
-
const requiredCapabilities = task.metadata?.requiredCapabilities || [];
|
|
797
|
-
const matchedCapabilities = requiredCapabilities.filter((cap)=>agent.capabilities.includes(cap)).length;
|
|
798
|
-
if (requiredCapabilities.length > 0 && matchedCapabilities === 0) {
|
|
799
|
-
return {
|
|
800
|
-
agent,
|
|
801
|
-
score: -1
|
|
802
|
-
}; // Can't handle task
|
|
803
|
-
}
|
|
804
|
-
score += matchedCapabilities * 5;
|
|
805
|
-
// Prefer agents with matching type
|
|
806
|
-
if (task.type === agent.type) {
|
|
807
|
-
score += 20;
|
|
808
|
-
}
|
|
809
|
-
return {
|
|
810
|
-
agent,
|
|
811
|
-
score
|
|
812
|
-
};
|
|
813
|
-
});
|
|
814
|
-
// Filter out agents that can't handle the task
|
|
815
|
-
const eligibleAgents = scoredAgents.filter(({ score })=>score >= 0);
|
|
816
|
-
if (eligibleAgents.length === 0) {
|
|
817
|
-
return undefined;
|
|
818
|
-
}
|
|
819
|
-
// Select agent with highest score
|
|
820
|
-
eligibleAgents.sort((a, b)=>b.score - a.score);
|
|
821
|
-
return eligibleAgents[0].agent;
|
|
822
|
-
}
|
|
823
|
-
async getComponentHealth(name, check) {
|
|
824
|
-
try {
|
|
825
|
-
const result = await Promise.race([
|
|
826
|
-
check(),
|
|
827
|
-
delay(5000).then(()=>({
|
|
828
|
-
healthy: false,
|
|
829
|
-
error: 'Health check timeout'
|
|
830
|
-
}))
|
|
831
|
-
]);
|
|
832
|
-
const health = {
|
|
833
|
-
name,
|
|
834
|
-
status: result.healthy ? 'healthy' : 'unhealthy',
|
|
835
|
-
lastCheck: new Date()
|
|
836
|
-
};
|
|
837
|
-
if (result.error !== undefined) {
|
|
838
|
-
health.error = result.error;
|
|
839
|
-
}
|
|
840
|
-
if ('metrics' in result && result.metrics !== undefined) {
|
|
841
|
-
health.metrics = result.metrics;
|
|
842
|
-
}
|
|
843
|
-
return health;
|
|
844
|
-
} catch (error) {
|
|
845
|
-
return {
|
|
846
|
-
name,
|
|
847
|
-
status: 'unhealthy',
|
|
848
|
-
lastCheck: new Date(),
|
|
849
|
-
error: error instanceof Error ? error.message : 'Unknown error'
|
|
850
|
-
};
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
processHealthResult(result, componentName) {
|
|
854
|
-
if (result.status === 'fulfilled') {
|
|
855
|
-
return result.value;
|
|
856
|
-
} else {
|
|
857
|
-
return {
|
|
858
|
-
name: componentName,
|
|
859
|
-
status: 'unhealthy',
|
|
860
|
-
lastCheck: new Date(),
|
|
861
|
-
error: result.reason?.message || 'Health check failed'
|
|
862
|
-
};
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
async initializeComponent(name, init) {
|
|
866
|
-
try {
|
|
867
|
-
await retry(init, {
|
|
868
|
-
maxAttempts: 3,
|
|
869
|
-
initialDelay: 2000
|
|
870
|
-
});
|
|
871
|
-
this.logger.info(`${name} initialized`);
|
|
872
|
-
} catch (error) {
|
|
873
|
-
this.logger.error(`Failed to initialize ${name}`, error);
|
|
874
|
-
throw new InitializationError(name, {
|
|
875
|
-
error
|
|
876
|
-
});
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
async shutdownComponent(name, shutdown) {
|
|
880
|
-
try {
|
|
881
|
-
await Promise.race([
|
|
882
|
-
shutdown(),
|
|
883
|
-
delay(10000)
|
|
884
|
-
]);
|
|
885
|
-
this.logger.info(`${name} shut down`);
|
|
886
|
-
} catch (error) {
|
|
887
|
-
this.logger.error(`Failed to shutdown ${name}`, error);
|
|
888
|
-
throw error;
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
validateAgentProfile(profile) {
|
|
892
|
-
if (!profile.id || !profile.name || !profile.type) {
|
|
893
|
-
throw new Error('Invalid agent profile: missing required fields');
|
|
894
|
-
}
|
|
895
|
-
if (profile.maxConcurrentTasks < 1) {
|
|
896
|
-
throw new Error('Invalid agent profile: maxConcurrentTasks must be at least 1');
|
|
897
|
-
}
|
|
898
|
-
if (this.agents.has(profile.id)) {
|
|
899
|
-
throw new Error(`Agent with ID ${profile.id} already exists`);
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
validateTask(task) {
|
|
903
|
-
if (!task.id || !task.type || !task.description) {
|
|
904
|
-
throw new Error('Invalid task: missing required fields');
|
|
905
|
-
}
|
|
906
|
-
if (task.priority < 0 || task.priority > 100) {
|
|
907
|
-
throw new Error('Invalid task: priority must be between 0 and 100');
|
|
908
|
-
}
|
|
909
|
-
if (this.taskHistory.has(task.id)) {
|
|
910
|
-
throw new Error(`Task with ID ${task.id} already exists`);
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
async handleAgentError(agentId, error) {
|
|
914
|
-
const profile = this.agents.get(agentId);
|
|
915
|
-
if (!profile) {
|
|
916
|
-
return;
|
|
917
|
-
}
|
|
918
|
-
// Log error details
|
|
919
|
-
this.logger.error('Handling agent error', {
|
|
920
|
-
agentId,
|
|
921
|
-
error
|
|
922
|
-
});
|
|
923
|
-
// Check if agent should be restarted
|
|
924
|
-
const errorCount = profile.metadata?.errorCount || 0;
|
|
925
|
-
profile.metadata = {
|
|
926
|
-
...profile.metadata,
|
|
927
|
-
errorCount: errorCount + 1
|
|
928
|
-
};
|
|
929
|
-
if (errorCount < 3) {
|
|
930
|
-
// Attempt to restart agent
|
|
931
|
-
try {
|
|
932
|
-
await this.terminateAgent(agentId);
|
|
933
|
-
await delay(2000); // Wait before restart
|
|
934
|
-
await this.spawnAgent({
|
|
935
|
-
...profile,
|
|
936
|
-
metadata: {
|
|
937
|
-
...profile.metadata,
|
|
938
|
-
errorCount: 0
|
|
939
|
-
}
|
|
940
|
-
});
|
|
941
|
-
this.logger.info('Agent restarted after error', {
|
|
942
|
-
agentId
|
|
943
|
-
});
|
|
944
|
-
} catch (restartError) {
|
|
945
|
-
this.logger.error('Failed to restart agent', {
|
|
946
|
-
agentId,
|
|
947
|
-
error: restartError
|
|
948
|
-
});
|
|
949
|
-
}
|
|
950
|
-
} else {
|
|
951
|
-
// Too many errors, terminate agent
|
|
952
|
-
this.logger.error('Agent exceeded error threshold, terminating', {
|
|
953
|
-
agentId,
|
|
954
|
-
errorCount
|
|
955
|
-
});
|
|
956
|
-
await this.terminateAgent(agentId);
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
async handleTaskFailure(taskId, error) {
|
|
960
|
-
const task = this.taskHistory.get(taskId);
|
|
961
|
-
if (!task) {
|
|
962
|
-
return;
|
|
963
|
-
}
|
|
964
|
-
const retryCount = task.metadata?.retryCount || 0;
|
|
965
|
-
const maxRetries = this.config.orchestrator.taskMaxRetries || 3;
|
|
966
|
-
if (retryCount < maxRetries) {
|
|
967
|
-
// Retry task
|
|
968
|
-
task.metadata = {
|
|
969
|
-
...task.metadata,
|
|
970
|
-
retryCount: retryCount + 1
|
|
971
|
-
};
|
|
972
|
-
task.status = 'queued';
|
|
973
|
-
delete task.assignedAgent;
|
|
974
|
-
// Add back to queue with delay
|
|
975
|
-
setTimeout(()=>{
|
|
976
|
-
this.taskQueue.push(task);
|
|
977
|
-
this.processTaskQueue();
|
|
978
|
-
}, Math.pow(2, retryCount) * 1000); // Exponential backoff
|
|
979
|
-
this.logger.info('Task queued for retry', {
|
|
980
|
-
taskId,
|
|
981
|
-
retryCount: retryCount + 1
|
|
982
|
-
});
|
|
983
|
-
} else {
|
|
984
|
-
this.logger.error('Task exceeded retry limit', {
|
|
985
|
-
taskId,
|
|
986
|
-
retryCount
|
|
987
|
-
});
|
|
988
|
-
}
|
|
989
|
-
}
|
|
990
|
-
handleSystemError(component, error) {
|
|
991
|
-
// Implement system-level error recovery strategies
|
|
992
|
-
this.logger.error('Handling system error', {
|
|
993
|
-
component,
|
|
994
|
-
error
|
|
995
|
-
});
|
|
996
|
-
// TODO: Implement specific recovery strategies based on component and error type
|
|
997
|
-
}
|
|
998
|
-
async resolveDeadlock(agents, resources) {
|
|
999
|
-
this.logger.warn('Resolving deadlock', {
|
|
1000
|
-
agents,
|
|
1001
|
-
resources
|
|
1002
|
-
});
|
|
1003
|
-
// Simple deadlock resolution: cancel lowest priority agent's tasks
|
|
1004
|
-
const agentProfiles = agents.map((id)=>this.agents.get(id)).filter(Boolean);
|
|
1005
|
-
if (agentProfiles.length === 0) {
|
|
1006
|
-
return;
|
|
1007
|
-
}
|
|
1008
|
-
// Sort by priority (lowest first)
|
|
1009
|
-
agentProfiles.sort((a, b)=>a.priority - b.priority);
|
|
1010
|
-
// Cancel tasks for lowest priority agent
|
|
1011
|
-
const targetAgent = agentProfiles[0];
|
|
1012
|
-
await this.cancelAgentTasks(targetAgent.id);
|
|
1013
|
-
this.logger.info('Deadlock resolved by cancelling tasks', {
|
|
1014
|
-
agentId: targetAgent.id
|
|
1015
|
-
});
|
|
1016
|
-
}
|
|
1017
|
-
async cancelAgentTasks(agentId) {
|
|
1018
|
-
try {
|
|
1019
|
-
const tasks = await this.coordinationManager.getAgentTasks(agentId);
|
|
1020
|
-
for (const task of tasks){
|
|
1021
|
-
await this.coordinationManager.cancelTask(task.id);
|
|
1022
|
-
// Update task status
|
|
1023
|
-
const trackedTask = this.taskHistory.get(task.id);
|
|
1024
|
-
if (trackedTask) {
|
|
1025
|
-
trackedTask.status = 'cancelled';
|
|
1026
|
-
trackedTask.completedAt = new Date();
|
|
1027
|
-
}
|
|
1028
|
-
this.eventBus.emit(SystemEvents.TASK_CANCELLED, {
|
|
1029
|
-
taskId: task.id,
|
|
1030
|
-
reason: 'Agent termination'
|
|
1031
|
-
});
|
|
1032
|
-
}
|
|
1033
|
-
} catch (error) {
|
|
1034
|
-
this.logger.error('Failed to cancel agent tasks', {
|
|
1035
|
-
agentId,
|
|
1036
|
-
error
|
|
1037
|
-
});
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
startAgentHealthMonitoring(agentId) {
|
|
1041
|
-
// TODO: Implement periodic health checks for individual agents
|
|
1042
|
-
}
|
|
1043
|
-
async recoverUnhealthyComponents(health) {
|
|
1044
|
-
for (const [name, component] of Object.entries(health.components)){
|
|
1045
|
-
if (component.status === 'unhealthy') {
|
|
1046
|
-
this.logger.warn('Attempting to recover unhealthy component', {
|
|
1047
|
-
name
|
|
1048
|
-
});
|
|
1049
|
-
// TODO: Implement component-specific recovery strategies
|
|
1050
|
-
switch(name){
|
|
1051
|
-
case 'Terminal Manager':
|
|
1052
|
-
break;
|
|
1053
|
-
case 'Memory Manager':
|
|
1054
|
-
break;
|
|
1055
|
-
case 'Coordination Manager':
|
|
1056
|
-
break;
|
|
1057
|
-
case 'MCP Server':
|
|
1058
|
-
break;
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
async cleanupTerminatedSessions() {
|
|
1064
|
-
const allSessions = this.sessionManager.getActiveSessions();
|
|
1065
|
-
const terminatedSessions = allSessions.filter((s)=>s.status === 'terminated');
|
|
1066
|
-
const cutoffTime = Date.now() - (this.config.orchestrator.sessionRetentionMs || 3600000); // 1 hour default
|
|
1067
|
-
for (const session of terminatedSessions){
|
|
1068
|
-
const typedSession = session;
|
|
1069
|
-
if (typedSession.endTime && typedSession.endTime.getTime() < cutoffTime) {
|
|
1070
|
-
await this.sessionManager.terminateSession(typedSession.id);
|
|
1071
|
-
this.logger.debug('Cleaned up old session', {
|
|
1072
|
-
sessionId: typedSession.id
|
|
1073
|
-
});
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
async cleanupTaskHistory() {
|
|
1078
|
-
const cutoffTime = Date.now() - (this.config.orchestrator.taskHistoryRetentionMs || 86400000); // 24 hours default
|
|
1079
|
-
for (const [taskId, task] of this.taskHistory.entries()){
|
|
1080
|
-
if (task.completedAt && task.completedAt.getTime() < cutoffTime) {
|
|
1081
|
-
this.taskHistory.delete(taskId);
|
|
1082
|
-
this.logger.debug('Cleaned up old task', {
|
|
1083
|
-
taskId
|
|
1084
|
-
});
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
async processShutdownTasks() {
|
|
1089
|
-
// Process any critical tasks before shutdown
|
|
1090
|
-
const criticalTasks = this.taskQueue.filter((t)=>t.priority >= 90 || t.metadata?.critical === true);
|
|
1091
|
-
if (criticalTasks.length > 0) {
|
|
1092
|
-
this.logger.info('Processing critical tasks before shutdown', {
|
|
1093
|
-
count: criticalTasks.length
|
|
1094
|
-
});
|
|
1095
|
-
// TODO: Implement critical task processing
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
|
-
/**
|
|
1099
|
-
* Get Claude API client instance
|
|
1100
|
-
*/ getClaudeClient() {
|
|
1101
|
-
return this.claudeClient;
|
|
1102
|
-
}
|
|
1103
|
-
/**
|
|
1104
|
-
* Update Claude API configuration dynamically
|
|
1105
|
-
*/ updateClaudeConfig(config) {
|
|
1106
|
-
this.configManager.setClaudeConfig(config);
|
|
1107
|
-
if (this.claudeClient) {
|
|
1108
|
-
this.claudeClient.updateConfig(config);
|
|
1109
|
-
} else if (this.configManager.isClaudeAPIConfigured()) {
|
|
1110
|
-
// Initialize Claude client with new config
|
|
1111
|
-
try {
|
|
1112
|
-
this.claudeClient = new ClaudeAPIClient(this.logger, this.configManager);
|
|
1113
|
-
this.logger.info('Claude API client initialized with new configuration');
|
|
1114
|
-
} catch (error) {
|
|
1115
|
-
this.logger.error('Failed to initialize Claude API client', error);
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
/**
|
|
1120
|
-
* Execute a Claude API request
|
|
1121
|
-
*/ async executeClaudeRequest(prompt, options) {
|
|
1122
|
-
if (!this.claudeClient) {
|
|
1123
|
-
this.logger.error('Claude API client not initialized');
|
|
1124
|
-
return null;
|
|
1125
|
-
}
|
|
1126
|
-
try {
|
|
1127
|
-
const response = await this.claudeClient.complete(prompt, options);
|
|
1128
|
-
return response;
|
|
1129
|
-
} catch (error) {
|
|
1130
|
-
this.logger.error('Claude API request failed', error);
|
|
1131
|
-
return null;
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
1135
|
-
|
|
1136
|
-
//# sourceMappingURL=orchestrator.js.map
|