claude-flow 1.0.0

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.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +612 -0
  3. package/bin/claude-flow +0 -0
  4. package/bin/claude-flow-simple +0 -0
  5. package/bin/claude-flow-typecheck +0 -0
  6. package/deno.json +84 -0
  7. package/package.json +45 -0
  8. package/scripts/check-links.ts +274 -0
  9. package/scripts/check-performance-regression.ts +168 -0
  10. package/scripts/claude-sparc.sh +562 -0
  11. package/scripts/coverage-report.ts +692 -0
  12. package/scripts/demo-task-system.ts +224 -0
  13. package/scripts/install.js +72 -0
  14. package/scripts/test-batch-tasks.ts +29 -0
  15. package/scripts/test-coordination-features.ts +238 -0
  16. package/scripts/test-mcp.ts +251 -0
  17. package/scripts/test-runner.ts +571 -0
  18. package/scripts/validate-examples.ts +288 -0
  19. package/src/cli/cli-core.ts +273 -0
  20. package/src/cli/commands/agent.ts +83 -0
  21. package/src/cli/commands/config.ts +442 -0
  22. package/src/cli/commands/help.ts +765 -0
  23. package/src/cli/commands/index.ts +963 -0
  24. package/src/cli/commands/mcp.ts +191 -0
  25. package/src/cli/commands/memory.ts +74 -0
  26. package/src/cli/commands/monitor.ts +403 -0
  27. package/src/cli/commands/session.ts +595 -0
  28. package/src/cli/commands/start.ts +156 -0
  29. package/src/cli/commands/status.ts +345 -0
  30. package/src/cli/commands/task.ts +79 -0
  31. package/src/cli/commands/workflow.ts +763 -0
  32. package/src/cli/completion.ts +553 -0
  33. package/src/cli/formatter.ts +310 -0
  34. package/src/cli/index.ts +211 -0
  35. package/src/cli/main.ts +23 -0
  36. package/src/cli/repl.ts +1050 -0
  37. package/src/cli/simple-cli.js +211 -0
  38. package/src/cli/simple-cli.ts +211 -0
  39. package/src/coordination/README.md +400 -0
  40. package/src/coordination/advanced-scheduler.ts +487 -0
  41. package/src/coordination/circuit-breaker.ts +366 -0
  42. package/src/coordination/conflict-resolution.ts +490 -0
  43. package/src/coordination/dependency-graph.ts +475 -0
  44. package/src/coordination/index.ts +63 -0
  45. package/src/coordination/manager.ts +460 -0
  46. package/src/coordination/messaging.ts +290 -0
  47. package/src/coordination/metrics.ts +585 -0
  48. package/src/coordination/resources.ts +322 -0
  49. package/src/coordination/scheduler.ts +390 -0
  50. package/src/coordination/work-stealing.ts +224 -0
  51. package/src/core/config.ts +627 -0
  52. package/src/core/event-bus.ts +186 -0
  53. package/src/core/json-persistence.ts +183 -0
  54. package/src/core/logger.ts +262 -0
  55. package/src/core/orchestrator-fixed.ts +312 -0
  56. package/src/core/orchestrator.ts +1234 -0
  57. package/src/core/persistence.ts +276 -0
  58. package/src/mcp/auth.ts +438 -0
  59. package/src/mcp/claude-flow-tools.ts +1280 -0
  60. package/src/mcp/load-balancer.ts +510 -0
  61. package/src/mcp/router.ts +240 -0
  62. package/src/mcp/server.ts +548 -0
  63. package/src/mcp/session-manager.ts +418 -0
  64. package/src/mcp/tools.ts +180 -0
  65. package/src/mcp/transports/base.ts +21 -0
  66. package/src/mcp/transports/http.ts +457 -0
  67. package/src/mcp/transports/stdio.ts +254 -0
  68. package/src/memory/backends/base.ts +22 -0
  69. package/src/memory/backends/markdown.ts +283 -0
  70. package/src/memory/backends/sqlite.ts +329 -0
  71. package/src/memory/cache.ts +238 -0
  72. package/src/memory/indexer.ts +238 -0
  73. package/src/memory/manager.ts +572 -0
  74. package/src/terminal/adapters/base.ts +29 -0
  75. package/src/terminal/adapters/native.ts +504 -0
  76. package/src/terminal/adapters/vscode.ts +340 -0
  77. package/src/terminal/manager.ts +308 -0
  78. package/src/terminal/pool.ts +271 -0
  79. package/src/terminal/session.ts +250 -0
  80. package/src/terminal/vscode-bridge.ts +242 -0
  81. package/src/utils/errors.ts +231 -0
  82. package/src/utils/helpers.ts +476 -0
  83. package/src/utils/types.ts +493 -0
@@ -0,0 +1,322 @@
1
+ /**
2
+ * Resource manager for preventing conflicts and deadlocks
3
+ */
4
+
5
+ import { Resource, CoordinationConfig, SystemEvents } from '../utils/types.ts';
6
+ import { IEventBus } from '../core/event-bus.ts';
7
+ import { ILogger } from '../core/logger.ts';
8
+ import { ResourceLockError } from '../utils/errors.ts';
9
+ import { delay, timeout } from '../utils/helpers.ts';
10
+
11
+ interface LockRequest {
12
+ agentId: string;
13
+ resourceId: string;
14
+ timestamp: Date;
15
+ priority: number;
16
+ }
17
+
18
+ /**
19
+ * Resource manager implementation
20
+ */
21
+ export class ResourceManager {
22
+ private resources = new Map<string, Resource>();
23
+ private locks = new Map<string, string>(); // resourceId -> agentId
24
+ private waitQueue = new Map<string, LockRequest[]>(); // resourceId -> queue
25
+ private agentResources = new Map<string, Set<string>>(); // agentId -> resourceIds
26
+
27
+ constructor(
28
+ private config: CoordinationConfig,
29
+ private eventBus: IEventBus,
30
+ private logger: ILogger,
31
+ ) {}
32
+
33
+ async initialize(): Promise<void> {
34
+ this.logger.info('Initializing resource manager');
35
+
36
+ // Set up periodic cleanup
37
+ setInterval(() => this.cleanup(), 30000); // Every 30 seconds
38
+ }
39
+
40
+ async shutdown(): Promise<void> {
41
+ this.logger.info('Shutting down resource manager');
42
+
43
+ // Release all locks
44
+ for (const [resourceId, agentId] of this.locks) {
45
+ await this.release(resourceId, agentId);
46
+ }
47
+
48
+ this.resources.clear();
49
+ this.locks.clear();
50
+ this.waitQueue.clear();
51
+ this.agentResources.clear();
52
+ }
53
+
54
+ async acquire(resourceId: string, agentId: string, priority = 0): Promise<void> {
55
+ this.logger.debug('Resource acquisition requested', { resourceId, agentId });
56
+
57
+ // Check if resource exists
58
+ if (!this.resources.has(resourceId)) {
59
+ this.resources.set(resourceId, {
60
+ id: resourceId,
61
+ type: 'generic',
62
+ locked: false,
63
+ });
64
+ }
65
+
66
+ const resource = this.resources.get(resourceId)!;
67
+
68
+ // Check if already locked by this agent
69
+ if (this.locks.get(resourceId) === agentId) {
70
+ this.logger.debug('Resource already locked by agent', { resourceId, agentId });
71
+ return;
72
+ }
73
+
74
+ // Try to acquire lock
75
+ if (!resource.locked) {
76
+ await this.lockResource(resourceId, agentId);
77
+ return;
78
+ }
79
+
80
+ // Add to wait queue
81
+ const request: LockRequest = {
82
+ agentId,
83
+ resourceId,
84
+ timestamp: new Date(),
85
+ priority,
86
+ };
87
+
88
+ if (!this.waitQueue.has(resourceId)) {
89
+ this.waitQueue.set(resourceId, []);
90
+ }
91
+
92
+ const queue = this.waitQueue.get(resourceId)!;
93
+ queue.push(request);
94
+
95
+ // Sort by priority and timestamp
96
+ queue.sort((a, b) => {
97
+ if (a.priority !== b.priority) {
98
+ return b.priority - a.priority; // Higher priority first
99
+ }
100
+ return a.timestamp.getTime() - b.timestamp.getTime(); // Earlier first
101
+ });
102
+
103
+ this.logger.info('Agent added to resource wait queue', {
104
+ resourceId,
105
+ agentId,
106
+ queueLength: queue.length,
107
+ });
108
+
109
+ // Wait for resource with timeout
110
+ const startTime = Date.now();
111
+ while (Date.now() - startTime < this.config.resourceTimeout) {
112
+ // Check if we're next in queue and resource is available
113
+ const nextRequest = queue[0];
114
+ if (nextRequest?.agentId === agentId && !resource.locked) {
115
+ // Remove from queue and acquire
116
+ queue.shift();
117
+ await this.lockResource(resourceId, agentId);
118
+ return;
119
+ }
120
+
121
+ // Check if our request is still in queue
122
+ const ourRequest = queue.find(req => req.agentId === agentId);
123
+ if (!ourRequest) {
124
+ // Request was removed (possibly by cleanup)
125
+ throw new ResourceLockError('Resource request cancelled');
126
+ }
127
+
128
+ await delay(100);
129
+ }
130
+
131
+ // Timeout - remove from queue
132
+ const index = queue.findIndex(req => req.agentId === agentId);
133
+ if (index !== -1) {
134
+ queue.splice(index, 1);
135
+ }
136
+
137
+ throw new ResourceLockError(
138
+ `Resource acquisition timeout for ${resourceId}`,
139
+ { resourceId, agentId, timeout: this.config.resourceTimeout },
140
+ );
141
+ }
142
+
143
+ async release(resourceId: string, agentId: string): Promise<void> {
144
+ this.logger.debug('Resource release requested', { resourceId, agentId });
145
+
146
+ const currentLock = this.locks.get(resourceId);
147
+ if (currentLock !== agentId) {
148
+ this.logger.warn('Attempted to release unowned resource', {
149
+ resourceId,
150
+ agentId,
151
+ currentLock,
152
+ });
153
+ return;
154
+ }
155
+
156
+ // Release the lock
157
+ this.unlockResource(resourceId, agentId);
158
+
159
+ // Process wait queue
160
+ const queue = this.waitQueue.get(resourceId);
161
+ if (queue && queue.length > 0) {
162
+ const nextRequest = queue.shift()!;
163
+
164
+ // Grant lock to next in queue
165
+ await this.lockResource(resourceId, nextRequest.agentId);
166
+ }
167
+ }
168
+
169
+ async releaseAllForAgent(agentId: string): Promise<void> {
170
+ const resources = this.agentResources.get(agentId);
171
+ if (!resources) {
172
+ return;
173
+ }
174
+
175
+ this.logger.info('Releasing all resources for agent', {
176
+ agentId,
177
+ resourceCount: resources.size,
178
+ });
179
+
180
+ const promises = Array.from(resources).map(
181
+ resourceId => this.release(resourceId, agentId),
182
+ );
183
+
184
+ await Promise.all(promises);
185
+ this.agentResources.delete(agentId);
186
+ }
187
+
188
+ getAllocations(): Map<string, string> {
189
+ return new Map(this.locks);
190
+ }
191
+
192
+ getWaitingRequests(): Map<string, string[]> {
193
+ const waiting = new Map<string, string[]>();
194
+
195
+ for (const [resourceId, queue] of this.waitQueue) {
196
+ if (queue.length > 0) {
197
+ waiting.set(
198
+ queue[0].agentId,
199
+ [...(waiting.get(queue[0].agentId) || []), resourceId],
200
+ );
201
+ }
202
+ }
203
+
204
+ return waiting;
205
+ }
206
+
207
+ async getHealthStatus(): Promise<{
208
+ healthy: boolean;
209
+ error?: string;
210
+ metrics?: Record<string, number>;
211
+ }> {
212
+ const totalResources = this.resources.size;
213
+ const lockedResources = this.locks.size;
214
+ const waitingAgents = new Set<string>();
215
+ let totalWaiting = 0;
216
+
217
+ for (const queue of this.waitQueue.values()) {
218
+ totalWaiting += queue.length;
219
+ queue.forEach(req => waitingAgents.add(req.agentId));
220
+ }
221
+
222
+ return {
223
+ healthy: true,
224
+ metrics: {
225
+ totalResources,
226
+ lockedResources,
227
+ freeResources: totalResources - lockedResources,
228
+ waitingAgents: waitingAgents.size,
229
+ totalWaitingRequests: totalWaiting,
230
+ },
231
+ };
232
+ }
233
+
234
+ private async lockResource(resourceId: string, agentId: string): Promise<void> {
235
+ const resource = this.resources.get(resourceId)!;
236
+
237
+ resource.locked = true;
238
+ resource.lockedBy = agentId;
239
+ resource.lockedAt = new Date();
240
+
241
+ this.locks.set(resourceId, agentId);
242
+
243
+ // Track agent resources
244
+ if (!this.agentResources.has(agentId)) {
245
+ this.agentResources.set(agentId, new Set());
246
+ }
247
+ this.agentResources.get(agentId)!.add(resourceId);
248
+
249
+ this.logger.info('Resource locked', { resourceId, agentId });
250
+
251
+ // Emit event
252
+ this.eventBus.emit(SystemEvents.RESOURCE_ACQUIRED, { resourceId, agentId });
253
+ }
254
+
255
+ private unlockResource(resourceId: string, agentId: string): void {
256
+ const resource = this.resources.get(resourceId);
257
+ if (!resource) {
258
+ return;
259
+ }
260
+
261
+ resource.locked = false;
262
+ delete resource.lockedBy;
263
+ delete resource.lockedAt;
264
+
265
+ this.locks.delete(resourceId);
266
+
267
+ // Remove from agent resources
268
+ this.agentResources.get(agentId)?.delete(resourceId);
269
+
270
+ this.logger.info('Resource unlocked', { resourceId, agentId });
271
+
272
+ // Emit event
273
+ this.eventBus.emit(SystemEvents.RESOURCE_RELEASED, { resourceId, agentId });
274
+ }
275
+
276
+ async performMaintenance(): Promise<void> {
277
+ this.logger.debug('Performing resource manager maintenance');
278
+ this.cleanup();
279
+ }
280
+
281
+ private cleanup(): void {
282
+ const now = Date.now();
283
+
284
+ // Clean up stale wait requests
285
+ for (const [resourceId, queue] of this.waitQueue) {
286
+ const filtered = queue.filter(req => {
287
+ const age = now - req.timestamp.getTime();
288
+ if (age > this.config.resourceTimeout) {
289
+ this.logger.warn('Removing stale resource request', {
290
+ resourceId,
291
+ agentId: req.agentId,
292
+ age,
293
+ });
294
+ return false;
295
+ }
296
+ return true;
297
+ });
298
+
299
+ if (filtered.length === 0) {
300
+ this.waitQueue.delete(resourceId);
301
+ } else {
302
+ this.waitQueue.set(resourceId, filtered);
303
+ }
304
+ }
305
+
306
+ // Clean up locks held too long
307
+ for (const [resourceId, agentId] of this.locks) {
308
+ const resource = this.resources.get(resourceId);
309
+ if (resource?.lockedAt) {
310
+ const lockAge = now - resource.lockedAt.getTime();
311
+ if (lockAge > this.config.resourceTimeout * 2) {
312
+ this.logger.warn('Force releasing stale lock', {
313
+ resourceId,
314
+ agentId,
315
+ lockAge,
316
+ });
317
+ this.unlockResource(resourceId, agentId);
318
+ }
319
+ }
320
+ }
321
+ }
322
+ }
@@ -0,0 +1,390 @@
1
+ /**
2
+ * Task scheduler implementation
3
+ */
4
+
5
+ import { Task, TaskStatus, CoordinationConfig, SystemEvents } from '../utils/types.ts';
6
+ import { IEventBus } from '../core/event-bus.ts';
7
+ import { ILogger } from '../core/logger.ts';
8
+ import { TaskError, TaskTimeoutError, TaskDependencyError } from '../utils/errors.ts';
9
+ import { delay } from '../utils/helpers.ts';
10
+
11
+ interface ScheduledTask {
12
+ task: Task;
13
+ agentId: string;
14
+ attempts: number;
15
+ lastAttempt?: Date;
16
+ timeout?: number;
17
+ }
18
+
19
+ /**
20
+ * Task scheduler for managing task assignment and execution
21
+ */
22
+ export class TaskScheduler {
23
+ protected tasks = new Map<string, ScheduledTask>();
24
+ protected agentTasks = new Map<string, Set<string>>(); // agentId -> taskIds
25
+ protected taskDependencies = new Map<string, Set<string>>(); // taskId -> dependent taskIds
26
+ protected completedTasks = new Set<string>();
27
+
28
+ constructor(
29
+ protected config: CoordinationConfig,
30
+ protected eventBus: IEventBus,
31
+ protected logger: ILogger,
32
+ ) {}
33
+
34
+ async initialize(): Promise<void> {
35
+ this.logger.info('Initializing task scheduler');
36
+
37
+ // Set up periodic cleanup
38
+ setInterval(() => this.cleanup(), 60000); // Every minute
39
+ }
40
+
41
+ async shutdown(): Promise<void> {
42
+ this.logger.info('Shutting down task scheduler');
43
+
44
+ // Cancel all active tasks
45
+ const taskIds = Array.from(this.tasks.keys());
46
+ await Promise.all(taskIds.map(id => this.cancelTask(id, 'Scheduler shutdown')));
47
+
48
+ this.tasks.clear();
49
+ this.agentTasks.clear();
50
+ this.taskDependencies.clear();
51
+ this.completedTasks.clear();
52
+ }
53
+
54
+ async assignTask(task: Task, agentId: string): Promise<void> {
55
+ this.logger.info('Assigning task', { taskId: task.id, agentId });
56
+
57
+ // Check dependencies
58
+ if (task.dependencies.length > 0) {
59
+ const unmetDependencies = task.dependencies.filter(
60
+ depId => !this.completedTasks.has(depId),
61
+ );
62
+
63
+ if (unmetDependencies.length > 0) {
64
+ throw new TaskDependencyError(task.id, unmetDependencies);
65
+ }
66
+ }
67
+
68
+ // Create scheduled task
69
+ const scheduledTask: ScheduledTask = {
70
+ task: { ...task, status: 'assigned', assignedAgent: agentId },
71
+ agentId,
72
+ attempts: 0,
73
+ };
74
+
75
+ // Store task
76
+ this.tasks.set(task.id, scheduledTask);
77
+
78
+ // Update agent tasks
79
+ if (!this.agentTasks.has(agentId)) {
80
+ this.agentTasks.set(agentId, new Set());
81
+ }
82
+ this.agentTasks.get(agentId)!.add(task.id);
83
+
84
+ // Update dependencies
85
+ for (const depId of task.dependencies) {
86
+ if (!this.taskDependencies.has(depId)) {
87
+ this.taskDependencies.set(depId, new Set());
88
+ }
89
+ this.taskDependencies.get(depId)!.add(task.id);
90
+ }
91
+
92
+ // Start task execution
93
+ this.startTask(task.id);
94
+ }
95
+
96
+ async completeTask(taskId: string, result: unknown): Promise<void> {
97
+ const scheduled = this.tasks.get(taskId);
98
+ if (!scheduled) {
99
+ throw new TaskError(`Task not found: ${taskId}`);
100
+ }
101
+
102
+ this.logger.info('Task completed', { taskId, agentId: scheduled.agentId });
103
+
104
+ // Update task status
105
+ scheduled.task.status = 'completed';
106
+ scheduled.task.output = result as Record<string, unknown>;
107
+ scheduled.task.completedAt = new Date();
108
+
109
+ // Clear timeout
110
+ if (scheduled.timeout) {
111
+ clearTimeout(scheduled.timeout);
112
+ }
113
+
114
+ // Remove from active tasks
115
+ this.tasks.delete(taskId);
116
+ this.agentTasks.get(scheduled.agentId)?.delete(taskId);
117
+
118
+ // Add to completed tasks
119
+ this.completedTasks.add(taskId);
120
+
121
+ // Check and start dependent tasks
122
+ const dependents = this.taskDependencies.get(taskId);
123
+ if (dependents) {
124
+ for (const dependentId of dependents) {
125
+ const dependent = this.tasks.get(dependentId);
126
+ if (dependent && this.canStartTask(dependent.task)) {
127
+ this.startTask(dependentId);
128
+ }
129
+ }
130
+ }
131
+ }
132
+
133
+ async failTask(taskId: string, error: Error): Promise<void> {
134
+ const scheduled = this.tasks.get(taskId);
135
+ if (!scheduled) {
136
+ throw new TaskError(`Task not found: ${taskId}`);
137
+ }
138
+
139
+ this.logger.error('Task failed', {
140
+ taskId,
141
+ agentId: scheduled.agentId,
142
+ attempt: scheduled.attempts,
143
+ error,
144
+ });
145
+
146
+ // Clear timeout
147
+ if (scheduled.timeout) {
148
+ clearTimeout(scheduled.timeout);
149
+ }
150
+
151
+ scheduled.attempts++;
152
+ scheduled.lastAttempt = new Date();
153
+
154
+ // Check if we should retry
155
+ if (scheduled.attempts < this.config.maxRetries) {
156
+ this.logger.info('Retrying task', {
157
+ taskId,
158
+ attempt: scheduled.attempts,
159
+ maxRetries: this.config.maxRetries,
160
+ });
161
+
162
+ // Schedule retry with exponential backoff
163
+ const retryDelay = this.config.retryDelay * Math.pow(2, scheduled.attempts - 1);
164
+
165
+ setTimeout(() => {
166
+ this.startTask(taskId);
167
+ }, retryDelay);
168
+ } else {
169
+ // Max retries exceeded, mark as failed
170
+ scheduled.task.status = 'failed';
171
+ scheduled.task.error = error;
172
+ scheduled.task.completedAt = new Date();
173
+
174
+ // Remove from active tasks
175
+ this.tasks.delete(taskId);
176
+ this.agentTasks.get(scheduled.agentId)?.delete(taskId);
177
+
178
+ // Cancel dependent tasks
179
+ await this.cancelDependentTasks(taskId, 'Parent task failed');
180
+ }
181
+ }
182
+
183
+ async cancelTask(taskId: string, reason: string): Promise<void> {
184
+ const scheduled = this.tasks.get(taskId);
185
+ if (!scheduled) {
186
+ return; // Already cancelled or completed
187
+ }
188
+
189
+ this.logger.info('Cancelling task', { taskId, reason });
190
+
191
+ // Clear timeout
192
+ if (scheduled.timeout) {
193
+ clearTimeout(scheduled.timeout);
194
+ }
195
+
196
+ // Update task status
197
+ scheduled.task.status = 'cancelled';
198
+ scheduled.task.completedAt = new Date();
199
+
200
+ // Emit cancellation event
201
+ this.eventBus.emit(SystemEvents.TASK_CANCELLED, { taskId, reason });
202
+
203
+ // Remove from active tasks
204
+ this.tasks.delete(taskId);
205
+ this.agentTasks.get(scheduled.agentId)?.delete(taskId);
206
+
207
+ // Cancel dependent tasks
208
+ await this.cancelDependentTasks(taskId, 'Parent task cancelled');
209
+ }
210
+
211
+ async cancelAgentTasks(agentId: string): Promise<void> {
212
+ const taskIds = this.agentTasks.get(agentId);
213
+ if (!taskIds) {
214
+ return;
215
+ }
216
+
217
+ this.logger.info('Cancelling all tasks for agent', {
218
+ agentId,
219
+ taskCount: taskIds.size,
220
+ });
221
+
222
+ const promises = Array.from(taskIds).map(
223
+ taskId => this.cancelTask(taskId, 'Agent terminated'),
224
+ );
225
+
226
+ await Promise.all(promises);
227
+ this.agentTasks.delete(agentId);
228
+ }
229
+
230
+ async rescheduleAgentTasks(agentId: string): Promise<void> {
231
+ const taskIds = this.agentTasks.get(agentId);
232
+ if (!taskIds || taskIds.size === 0) {
233
+ return;
234
+ }
235
+
236
+ this.logger.info('Rescheduling tasks for agent', {
237
+ agentId,
238
+ taskCount: taskIds.size,
239
+ });
240
+
241
+ for (const taskId of taskIds) {
242
+ const scheduled = this.tasks.get(taskId);
243
+ if (scheduled && scheduled.task.status === 'running') {
244
+ // Reset task status
245
+ scheduled.task.status = 'queued';
246
+ scheduled.attempts = 0;
247
+
248
+ // Re-emit task created event for reassignment
249
+ this.eventBus.emit(SystemEvents.TASK_CREATED, {
250
+ task: scheduled.task,
251
+ });
252
+ }
253
+ }
254
+ }
255
+
256
+ getAgentTaskCount(agentId: string): number {
257
+ return this.agentTasks.get(agentId)?.size || 0;
258
+ }
259
+
260
+ async getHealthStatus(): Promise<{
261
+ healthy: boolean;
262
+ error?: string;
263
+ metrics?: Record<string, number>;
264
+ }> {
265
+ const activeTasks = this.tasks.size;
266
+ const completedTasks = this.completedTasks.size;
267
+ const agentsWithTasks = this.agentTasks.size;
268
+
269
+ const tasksByStatus: Record<TaskStatus, number> = {
270
+ pending: 0,
271
+ queued: 0,
272
+ assigned: 0,
273
+ running: 0,
274
+ completed: completedTasks,
275
+ failed: 0,
276
+ cancelled: 0,
277
+ };
278
+
279
+ for (const scheduled of this.tasks.values()) {
280
+ tasksByStatus[scheduled.task.status]++;
281
+ }
282
+
283
+ return {
284
+ healthy: true,
285
+ metrics: {
286
+ activeTasks,
287
+ completedTasks,
288
+ agentsWithTasks,
289
+ ...tasksByStatus,
290
+ },
291
+ };
292
+ }
293
+
294
+ async getAgentTasks(agentId: string): Promise<Task[]> {
295
+ const taskIds = this.agentTasks.get(agentId);
296
+ if (!taskIds) {
297
+ return [];
298
+ }
299
+
300
+ const tasks: Task[] = [];
301
+ for (const taskId of taskIds) {
302
+ const scheduled = this.tasks.get(taskId);
303
+ if (scheduled) {
304
+ tasks.push(scheduled.task);
305
+ }
306
+ }
307
+
308
+ return tasks;
309
+ }
310
+
311
+ async performMaintenance(): Promise<void> {
312
+ this.logger.debug('Performing task scheduler maintenance');
313
+
314
+ // Cleanup old completed tasks
315
+ this.cleanup();
316
+
317
+ // Check for stuck tasks
318
+ const now = new Date();
319
+ for (const [taskId, scheduled] of this.tasks) {
320
+ if (scheduled.task.status === 'running' && scheduled.task.startedAt) {
321
+ const runtime = now.getTime() - scheduled.task.startedAt.getTime();
322
+ if (runtime > this.config.resourceTimeout * 2) {
323
+ this.logger.warn('Found stuck task', {
324
+ taskId,
325
+ runtime,
326
+ agentId: scheduled.agentId,
327
+ });
328
+
329
+ // Force fail the task
330
+ await this.failTask(taskId, new TaskTimeoutError(taskId, runtime));
331
+ }
332
+ }
333
+ }
334
+ }
335
+
336
+ private startTask(taskId: string): void {
337
+ const scheduled = this.tasks.get(taskId);
338
+ if (!scheduled) {
339
+ return;
340
+ }
341
+
342
+ // Update status
343
+ scheduled.task.status = 'running';
344
+ scheduled.task.startedAt = new Date();
345
+
346
+ // Emit task started event
347
+ this.eventBus.emit(SystemEvents.TASK_STARTED, {
348
+ taskId,
349
+ agentId: scheduled.agentId,
350
+ });
351
+
352
+ // Set timeout for task execution
353
+ const timeoutMs = this.config.resourceTimeout;
354
+ scheduled.timeout = setTimeout(() => {
355
+ this.failTask(taskId, new TaskTimeoutError(taskId, timeoutMs));
356
+ }, timeoutMs);
357
+ }
358
+
359
+ private canStartTask(task: Task): boolean {
360
+ // Check if all dependencies are completed
361
+ return task.dependencies.every(depId => this.completedTasks.has(depId));
362
+ }
363
+
364
+ private async cancelDependentTasks(taskId: string, reason: string): Promise<void> {
365
+ const dependents = this.taskDependencies.get(taskId);
366
+ if (!dependents) {
367
+ return;
368
+ }
369
+
370
+ for (const dependentId of dependents) {
371
+ await this.cancelTask(dependentId, reason);
372
+ }
373
+ }
374
+
375
+ private cleanup(): void {
376
+ // Clean up old completed tasks (keep last 1000)
377
+ if (this.completedTasks.size > 1000) {
378
+ const toRemove = this.completedTasks.size - 1000;
379
+ const iterator = this.completedTasks.values();
380
+
381
+ for (let i = 0; i < toRemove; i++) {
382
+ const result = iterator.next();
383
+ if (!result.done && result.value) {
384
+ this.completedTasks.delete(result.value);
385
+ this.taskDependencies.delete(result.value);
386
+ }
387
+ }
388
+ }
389
+ }
390
+ }