@claude-flow/swarm 3.0.0-alpha.7 → 3.0.0-alpha.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.
Files changed (130) hide show
  1. package/dist/agent-pool.d.ts.map +1 -1
  2. package/dist/agent-pool.js +10 -2
  3. package/dist/agent-pool.js.map +1 -1
  4. package/dist/attention-coordinator.d.ts +5 -4
  5. package/dist/attention-coordinator.d.ts.map +1 -1
  6. package/dist/attention-coordinator.js +17 -10
  7. package/dist/attention-coordinator.js.map +1 -1
  8. package/dist/consensus/byzantine.d.ts +2 -1
  9. package/dist/consensus/byzantine.d.ts.map +1 -1
  10. package/dist/consensus/byzantine.js +34 -8
  11. package/dist/consensus/byzantine.js.map +1 -1
  12. package/dist/consensus/gossip.d.ts +2 -1
  13. package/dist/consensus/gossip.d.ts.map +1 -1
  14. package/dist/consensus/gossip.js +27 -8
  15. package/dist/consensus/gossip.js.map +1 -1
  16. package/dist/consensus/index.d.ts +1 -1
  17. package/dist/consensus/index.d.ts.map +1 -1
  18. package/dist/consensus/index.js +2 -2
  19. package/dist/consensus/index.js.map +1 -1
  20. package/dist/consensus/raft.d.ts +2 -1
  21. package/dist/consensus/raft.d.ts.map +1 -1
  22. package/dist/consensus/raft.js +25 -9
  23. package/dist/consensus/raft.js.map +1 -1
  24. package/dist/coordination/agent-registry.d.ts +2 -2
  25. package/dist/coordination/agent-registry.d.ts.map +1 -1
  26. package/dist/coordination/agent-registry.js +1 -1
  27. package/dist/coordination/agent-registry.js.map +1 -1
  28. package/dist/coordination/swarm-hub.d.ts +5 -5
  29. package/dist/coordination/swarm-hub.d.ts.map +1 -1
  30. package/dist/coordination/swarm-hub.js +5 -5
  31. package/dist/coordination/swarm-hub.js.map +1 -1
  32. package/dist/coordination/task-orchestrator.d.ts +3 -3
  33. package/dist/coordination/task-orchestrator.d.ts.map +1 -1
  34. package/dist/coordination/task-orchestrator.js +1 -1
  35. package/dist/coordination/task-orchestrator.js.map +1 -1
  36. package/dist/index.d.ts +2 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +4 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/message-bus.d.ts.map +1 -1
  41. package/dist/message-bus.js +13 -4
  42. package/dist/message-bus.js.map +1 -1
  43. package/dist/queen-coordinator.d.ts +1 -1
  44. package/dist/queen-coordinator.d.ts.map +1 -1
  45. package/dist/queen-coordinator.js +10 -3
  46. package/dist/queen-coordinator.js.map +1 -1
  47. package/dist/topology-manager.d.ts.map +1 -1
  48. package/dist/topology-manager.js +26 -1
  49. package/dist/topology-manager.js.map +1 -1
  50. package/dist/types.d.ts +3 -2
  51. package/dist/types.d.ts.map +1 -1
  52. package/dist/types.js.map +1 -1
  53. package/dist/unified-coordinator.d.ts +1 -1
  54. package/dist/unified-coordinator.d.ts.map +1 -1
  55. package/dist/unified-coordinator.js +85 -29
  56. package/dist/unified-coordinator.js.map +1 -1
  57. package/package.json +7 -2
  58. package/.claude-flow/data/pending-insights.jsonl +0 -12
  59. package/__tests__/byzantine-transport.test.ts +0 -147
  60. package/__tests__/consensus-failure-injection.test.ts +0 -161
  61. package/__tests__/consensus-transport.test.ts +0 -200
  62. package/__tests__/consensus.test.ts +0 -577
  63. package/__tests__/coordinator.test.ts +0 -501
  64. package/__tests__/federation-transport.test.ts +0 -145
  65. package/__tests__/gossip-transport.test.ts +0 -81
  66. package/__tests__/queen-coordinator.test.ts +0 -1335
  67. package/__tests__/raft-transport.test.ts +0 -121
  68. package/__tests__/topology.test.ts +0 -621
  69. package/ruvector.db +0 -0
  70. package/src/agent-pool.ts +0 -476
  71. package/src/application/commands/create-task.command.ts +0 -124
  72. package/src/application/commands/spawn-agent.command.ts +0 -122
  73. package/src/application/index.ts +0 -30
  74. package/src/application/services/swarm-application-service.ts +0 -200
  75. package/src/attention-coordinator.ts +0 -1000
  76. package/src/consensus/byzantine.ts +0 -514
  77. package/src/consensus/federation-transport.ts +0 -185
  78. package/src/consensus/gossip.ts +0 -599
  79. package/src/consensus/index.ts +0 -296
  80. package/src/consensus/raft.ts +0 -561
  81. package/src/consensus/transport.ts +0 -284
  82. package/src/coordination/agent-registry.ts +0 -544
  83. package/src/coordination/index.ts +0 -23
  84. package/src/coordination/swarm-hub.ts +0 -776
  85. package/src/coordination/task-orchestrator.ts +0 -605
  86. package/src/domain/entities/agent.d.ts +0 -151
  87. package/src/domain/entities/agent.d.ts.map +0 -1
  88. package/src/domain/entities/agent.js +0 -280
  89. package/src/domain/entities/agent.js.map +0 -1
  90. package/src/domain/entities/agent.ts +0 -370
  91. package/src/domain/entities/task.d.ts +0 -133
  92. package/src/domain/entities/task.d.ts.map +0 -1
  93. package/src/domain/entities/task.js +0 -261
  94. package/src/domain/entities/task.js.map +0 -1
  95. package/src/domain/entities/task.ts +0 -319
  96. package/src/domain/index.ts +0 -41
  97. package/src/domain/repositories/agent-repository.interface.d.ts +0 -57
  98. package/src/domain/repositories/agent-repository.interface.d.ts.map +0 -1
  99. package/src/domain/repositories/agent-repository.interface.js +0 -9
  100. package/src/domain/repositories/agent-repository.interface.js.map +0 -1
  101. package/src/domain/repositories/agent-repository.interface.ts +0 -69
  102. package/src/domain/repositories/task-repository.interface.d.ts +0 -61
  103. package/src/domain/repositories/task-repository.interface.d.ts.map +0 -1
  104. package/src/domain/repositories/task-repository.interface.js +0 -9
  105. package/src/domain/repositories/task-repository.interface.js.map +0 -1
  106. package/src/domain/repositories/task-repository.interface.ts +0 -75
  107. package/src/domain/services/coordination-service.ts +0 -320
  108. package/src/federation-hub.d.ts +0 -284
  109. package/src/federation-hub.d.ts.map +0 -1
  110. package/src/federation-hub.js +0 -692
  111. package/src/federation-hub.js.map +0 -1
  112. package/src/federation-hub.ts +0 -979
  113. package/src/index.ts +0 -348
  114. package/src/message-bus.ts +0 -607
  115. package/src/queen-coordinator.ts +0 -2025
  116. package/src/shared/events.ts +0 -285
  117. package/src/shared/types.ts +0 -389
  118. package/src/topology-manager.ts +0 -656
  119. package/src/types.ts +0 -545
  120. package/src/unified-coordinator.ts +0 -1844
  121. package/src/workers/index.ts +0 -65
  122. package/src/workers/worker-dispatch.d.ts +0 -234
  123. package/src/workers/worker-dispatch.d.ts.map +0 -1
  124. package/src/workers/worker-dispatch.js +0 -842
  125. package/src/workers/worker-dispatch.js.map +0 -1
  126. package/src/workers/worker-dispatch.ts +0 -1076
  127. package/tmp.json +0 -0
  128. package/tsconfig.json +0 -9
  129. package/tsconfig.tsbuildinfo +0 -1
  130. package/vitest.config.ts +0 -20
@@ -1,656 +0,0 @@
1
- /**
2
- * V3 Topology Manager
3
- * Manages swarm network topology with support for mesh, hierarchical, centralized, and hybrid modes
4
- */
5
-
6
- import { EventEmitter } from 'events';
7
- import {
8
- TopologyConfig,
9
- TopologyState,
10
- TopologyNode,
11
- TopologyEdge,
12
- TopologyPartition,
13
- TopologyType,
14
- ITopologyManager,
15
- } from './types.js';
16
-
17
- export class TopologyManager extends EventEmitter implements ITopologyManager {
18
- private config: TopologyConfig;
19
- private state: TopologyState;
20
- private nodeIndex: Map<string, TopologyNode> = new Map();
21
- private adjacencyList: Map<string, Set<string>> = new Map();
22
- private lastRebalance: Date = new Date();
23
-
24
- // O(1) role-based indexes for performance (fixes O(n) find operations)
25
- private roleIndex: Map<TopologyNode['role'], Set<string>> = new Map();
26
- private queenNode: TopologyNode | null = null;
27
- private coordinatorNode: TopologyNode | null = null;
28
-
29
- constructor(config: Partial<TopologyConfig> = {}) {
30
- super();
31
- this.config = {
32
- type: config.type ?? 'mesh',
33
- maxAgents: config.maxAgents ?? 100,
34
- replicationFactor: config.replicationFactor ?? 2,
35
- partitionStrategy: config.partitionStrategy ?? 'hash',
36
- failoverEnabled: config.failoverEnabled ?? true,
37
- autoRebalance: config.autoRebalance ?? true,
38
- };
39
-
40
- this.state = {
41
- type: this.config.type,
42
- nodes: [],
43
- edges: [],
44
- leader: undefined,
45
- partitions: [],
46
- };
47
- }
48
-
49
- async initialize(config?: TopologyConfig): Promise<void> {
50
- if (config) {
51
- this.config = { ...this.config, ...config };
52
- this.state.type = this.config.type;
53
- }
54
-
55
- this.emit('initialized', { type: this.config.type });
56
- }
57
-
58
- getState(): TopologyState {
59
- return {
60
- ...this.state,
61
- nodes: [...this.state.nodes],
62
- edges: [...this.state.edges],
63
- partitions: [...this.state.partitions],
64
- };
65
- }
66
-
67
- async addNode(agentId: string, role: TopologyNode['role']): Promise<TopologyNode> {
68
- if (this.nodeIndex.has(agentId)) {
69
- throw new Error(`Node ${agentId} already exists in topology`);
70
- }
71
-
72
- if (this.nodeIndex.size >= this.config.maxAgents) {
73
- throw new Error(`Maximum agents (${this.config.maxAgents}) reached`);
74
- }
75
-
76
- // Create node with connections based on topology type
77
- const connections = this.calculateInitialConnections(agentId, role);
78
-
79
- const node: TopologyNode = {
80
- id: `node_${agentId}`,
81
- agentId,
82
- role: this.determineRole(role),
83
- status: 'syncing',
84
- connections,
85
- metadata: {
86
- joinedAt: new Date().toISOString(),
87
- version: '1.0.0',
88
- },
89
- };
90
-
91
- // Add to state
92
- this.nodeIndex.set(agentId, node);
93
- this.state.nodes.push(node);
94
-
95
- // Update role index for O(1) role-based lookups
96
- this.addToRoleIndex(node);
97
-
98
- // Initialize adjacency list
99
- this.adjacencyList.set(agentId, new Set(connections));
100
-
101
- // Create edges
102
- await this.createEdgesForNode(node);
103
-
104
- // Update partitions if needed
105
- await this.updatePartitions(node);
106
-
107
- // Mark as active after sync
108
- node.status = 'active';
109
-
110
- this.emit('node.added', { node });
111
-
112
- // Trigger rebalance if needed
113
- if (this.config.autoRebalance && this.shouldRebalance()) {
114
- await this.rebalance();
115
- }
116
-
117
- return node;
118
- }
119
-
120
- async removeNode(agentId: string): Promise<void> {
121
- const node = this.nodeIndex.get(agentId);
122
- if (!node) {
123
- return;
124
- }
125
-
126
- // Remove from state
127
- this.state.nodes = this.state.nodes.filter(n => n.agentId !== agentId);
128
- this.nodeIndex.delete(agentId);
129
-
130
- // Update role index
131
- this.removeFromRoleIndex(node);
132
-
133
- // Remove all edges connected to this node
134
- this.state.edges = this.state.edges.filter(
135
- e => e.from !== agentId && e.to !== agentId
136
- );
137
-
138
- // Update adjacency list
139
- this.adjacencyList.delete(agentId);
140
- for (const neighbors of this.adjacencyList.values()) {
141
- neighbors.delete(agentId);
142
- }
143
-
144
- // Update all nodes' connections
145
- for (const n of this.state.nodes) {
146
- n.connections = n.connections.filter(c => c !== agentId);
147
- }
148
-
149
- // If this was the leader, elect new one
150
- if (this.state.leader === agentId) {
151
- await this.electLeader();
152
- }
153
-
154
- // Update partitions
155
- for (const partition of this.state.partitions) {
156
- partition.nodes = partition.nodes.filter(n => n !== agentId);
157
- if (partition.leader === agentId) {
158
- partition.leader = partition.nodes[0] || '';
159
- }
160
- }
161
-
162
- this.emit('node.removed', { agentId });
163
-
164
- // Trigger rebalance if needed
165
- if (this.config.autoRebalance) {
166
- await this.rebalance();
167
- }
168
- }
169
-
170
- async updateNode(agentId: string, updates: Partial<TopologyNode>): Promise<void> {
171
- const node = this.nodeIndex.get(agentId);
172
- if (!node) {
173
- throw new Error(`Node ${agentId} not found`);
174
- }
175
-
176
- // Apply updates
177
- if (updates.role !== undefined) node.role = updates.role;
178
- if (updates.status !== undefined) node.status = updates.status;
179
- if (updates.connections !== undefined) {
180
- node.connections = updates.connections;
181
- this.adjacencyList.set(agentId, new Set(updates.connections));
182
- }
183
- if (updates.metadata !== undefined) {
184
- node.metadata = { ...node.metadata, ...updates.metadata };
185
- }
186
-
187
- this.emit('node.updated', { agentId, updates });
188
- }
189
-
190
- getLeader(): string | undefined {
191
- return this.state.leader;
192
- }
193
-
194
- async electLeader(): Promise<string> {
195
- if (this.state.nodes.length === 0) {
196
- throw new Error('No nodes available for leader election');
197
- }
198
-
199
- // For hierarchical topology, the queen is the leader (O(1) lookup)
200
- if (this.config.type === 'hierarchical') {
201
- const queen = this.queenNode;
202
- if (queen) {
203
- this.state.leader = queen.agentId;
204
- return queen.agentId;
205
- }
206
- }
207
-
208
- // For centralized topology, the coordinator is the leader (O(1) lookup)
209
- if (this.config.type === 'centralized') {
210
- const coordinator = this.coordinatorNode;
211
- if (coordinator) {
212
- this.state.leader = coordinator.agentId;
213
- return coordinator.agentId;
214
- }
215
- }
216
-
217
- // For mesh/hybrid, elect based on node capabilities
218
- const candidates = this.state.nodes
219
- .filter(n => n.status === 'active')
220
- .sort((a, b) => {
221
- // Prefer coordinators, then queens
222
- const roleOrder: Record<TopologyNode['role'], number> = {
223
- queen: 0,
224
- coordinator: 1,
225
- worker: 2,
226
- peer: 2,
227
- };
228
- return roleOrder[a.role] - roleOrder[b.role];
229
- });
230
-
231
- if (candidates.length === 0) {
232
- throw new Error('No active nodes available for leader election');
233
- }
234
-
235
- const leader = candidates[0];
236
- this.state.leader = leader.agentId;
237
-
238
- this.emit('leader.elected', { leaderId: leader.agentId });
239
-
240
- return leader.agentId;
241
- }
242
-
243
- async rebalance(): Promise<void> {
244
- const now = new Date();
245
- const timeSinceLastRebalance = now.getTime() - this.lastRebalance.getTime();
246
-
247
- // Prevent too frequent rebalancing
248
- if (timeSinceLastRebalance < 5000) {
249
- return;
250
- }
251
-
252
- this.lastRebalance = now;
253
-
254
- switch (this.config.type) {
255
- case 'mesh':
256
- await this.rebalanceMesh();
257
- break;
258
- case 'hierarchical':
259
- await this.rebalanceHierarchical();
260
- break;
261
- case 'centralized':
262
- await this.rebalanceCentralized();
263
- break;
264
- case 'hybrid':
265
- await this.rebalanceHybrid();
266
- break;
267
- }
268
-
269
- this.emit('topology.rebalanced', { type: this.config.type });
270
- }
271
-
272
- getNeighbors(agentId: string): string[] {
273
- return Array.from(this.adjacencyList.get(agentId) || []);
274
- }
275
-
276
- findOptimalPath(from: string, to: string): string[] {
277
- if (from === to) {
278
- return [from];
279
- }
280
-
281
- // BFS for shortest path
282
- const visited = new Set<string>();
283
- const queue: { node: string; path: string[] }[] = [{ node: from, path: [from] }];
284
-
285
- while (queue.length > 0) {
286
- const { node, path } = queue.shift()!;
287
-
288
- if (node === to) {
289
- return path;
290
- }
291
-
292
- if (visited.has(node)) {
293
- continue;
294
- }
295
- visited.add(node);
296
-
297
- const neighbors = this.adjacencyList.get(node) || new Set();
298
- for (const neighbor of neighbors) {
299
- if (!visited.has(neighbor)) {
300
- queue.push({ node: neighbor, path: [...path, neighbor] });
301
- }
302
- }
303
- }
304
-
305
- // No path found
306
- return [];
307
- }
308
-
309
- // ===== PRIVATE METHODS =====
310
-
311
- private determineRole(requestedRole: TopologyNode['role']): TopologyNode['role'] {
312
- switch (this.config.type) {
313
- case 'mesh':
314
- return 'peer';
315
- case 'hierarchical':
316
- // First node becomes queen
317
- if (this.state.nodes.length === 0) {
318
- return 'queen';
319
- }
320
- return requestedRole === 'queen' && !this.hasQueen() ? 'queen' : 'worker';
321
- case 'centralized':
322
- // First node becomes coordinator
323
- if (this.state.nodes.length === 0) {
324
- return 'coordinator';
325
- }
326
- return 'worker';
327
- case 'hybrid':
328
- return requestedRole;
329
- }
330
- }
331
-
332
- private hasQueen(): boolean {
333
- return this.queenNode !== null; // O(1) check using cached queen
334
- }
335
-
336
- private calculateInitialConnections(agentId: string, role: TopologyNode['role']): string[] {
337
- const existingNodes = Array.from(this.nodeIndex.keys());
338
-
339
- switch (this.config.type) {
340
- case 'mesh':
341
- // In mesh, connect to all existing nodes (up to a limit)
342
- const maxMeshConnections = Math.min(10, existingNodes.length);
343
- return existingNodes.slice(0, maxMeshConnections);
344
-
345
- case 'hierarchical':
346
- // Workers connect to queen, queen connects to workers (O(1) lookup)
347
- if (role === 'queen' || existingNodes.length === 0) {
348
- return existingNodes;
349
- }
350
- return this.queenNode ? [this.queenNode.agentId] : [];
351
-
352
- case 'centralized':
353
- // All nodes connect to coordinator (O(1) lookup)
354
- if (role === 'coordinator' || existingNodes.length === 0) {
355
- return existingNodes;
356
- }
357
- return this.coordinatorNode ? [this.coordinatorNode.agentId] : [];
358
-
359
- case 'hybrid':
360
- // Mix of mesh and hierarchical
361
- const leaders = this.state.nodes.filter(n =>
362
- n.role === 'queen' || n.role === 'coordinator'
363
- );
364
- const peers = existingNodes.slice(0, 3);
365
- return [...new Set([...leaders.map(l => l.agentId), ...peers])];
366
- }
367
- }
368
-
369
- private async createEdgesForNode(node: TopologyNode): Promise<void> {
370
- for (const connectionId of node.connections) {
371
- const edge: TopologyEdge = {
372
- from: node.agentId,
373
- to: connectionId,
374
- weight: 1,
375
- bidirectional: this.config.type === 'mesh',
376
- latencyMs: 0,
377
- };
378
-
379
- this.state.edges.push(edge);
380
-
381
- // Add bidirectional edge
382
- if (edge.bidirectional) {
383
- const existingNode = this.nodeIndex.get(connectionId);
384
- if (existingNode && !existingNode.connections.includes(node.agentId)) {
385
- existingNode.connections.push(node.agentId);
386
- this.adjacencyList.get(connectionId)?.add(node.agentId);
387
- }
388
- }
389
- }
390
- }
391
-
392
- private async updatePartitions(node: TopologyNode): Promise<void> {
393
- if (this.config.type !== 'mesh' && this.config.type !== 'hybrid') {
394
- return;
395
- }
396
-
397
- // Create partitions based on strategy
398
- const nodesPerPartition = Math.ceil(this.config.maxAgents / 10);
399
- const partitionIndex = Math.floor(this.state.nodes.length / nodesPerPartition);
400
-
401
- if (this.state.partitions.length <= partitionIndex) {
402
- // Create new partition
403
- const partition: TopologyPartition = {
404
- id: `partition_${partitionIndex}`,
405
- nodes: [node.agentId],
406
- leader: node.agentId,
407
- replicaCount: 1,
408
- };
409
- this.state.partitions.push(partition);
410
- } else {
411
- // Add to existing partition
412
- const partition = this.state.partitions[partitionIndex];
413
- partition.nodes.push(node.agentId);
414
- partition.replicaCount = Math.min(
415
- partition.nodes.length,
416
- this.config.replicationFactor ?? 2
417
- );
418
- }
419
- }
420
-
421
- private shouldRebalance(): boolean {
422
- // Check for uneven distribution
423
- if (this.config.type === 'mesh') {
424
- const avgConnections = this.state.nodes.reduce(
425
- (sum, n) => sum + n.connections.length, 0
426
- ) / Math.max(1, this.state.nodes.length);
427
-
428
- for (const node of this.state.nodes) {
429
- if (Math.abs(node.connections.length - avgConnections) > avgConnections * 0.5) {
430
- return true;
431
- }
432
- }
433
- }
434
-
435
- return false;
436
- }
437
-
438
- private async rebalanceMesh(): Promise<void> {
439
- const targetConnections = Math.min(5, this.state.nodes.length - 1);
440
-
441
- for (const node of this.state.nodes) {
442
- // Ensure minimum connections
443
- while (node.connections.length < targetConnections) {
444
- const candidates = this.state.nodes
445
- .filter(n =>
446
- n.agentId !== node.agentId &&
447
- !node.connections.includes(n.agentId)
448
- )
449
- .sort(() => Math.random() - 0.5);
450
-
451
- if (candidates.length > 0) {
452
- node.connections.push(candidates[0].agentId);
453
- this.adjacencyList.get(node.agentId)?.add(candidates[0].agentId);
454
-
455
- // Bidirectional
456
- candidates[0].connections.push(node.agentId);
457
- this.adjacencyList.get(candidates[0].agentId)?.add(node.agentId);
458
- } else {
459
- break;
460
- }
461
- }
462
- }
463
- }
464
-
465
- private async rebalanceHierarchical(): Promise<void> {
466
- // O(1) queen lookup
467
- let queen = this.queenNode;
468
- if (!queen) {
469
- // Elect a queen if missing
470
- if (this.state.nodes.length > 0) {
471
- const newQueen = this.state.nodes[0]!;
472
- newQueen.role = 'queen';
473
- this.addToRoleIndex(newQueen);
474
- queen = newQueen;
475
- } else {
476
- return;
477
- }
478
- }
479
-
480
- // Ensure all workers are connected to queen
481
- for (const node of this.state.nodes) {
482
- if (node.role === 'worker' && !node.connections.includes(queen.agentId)) {
483
- node.connections.push(queen.agentId);
484
- this.adjacencyList.get(node.agentId)?.add(queen.agentId);
485
- queen.connections.push(node.agentId);
486
- this.adjacencyList.get(queen.agentId)?.add(node.agentId);
487
- }
488
- }
489
- }
490
-
491
- private async rebalanceCentralized(): Promise<void> {
492
- // O(1) coordinator lookup
493
- let coordinator = this.coordinatorNode;
494
- if (!coordinator) {
495
- if (this.state.nodes.length > 0) {
496
- const newCoord = this.state.nodes[0]!;
497
- newCoord.role = 'coordinator';
498
- this.addToRoleIndex(newCoord);
499
- coordinator = newCoord;
500
- } else {
501
- return;
502
- }
503
- }
504
-
505
- // Ensure all nodes are connected to coordinator
506
- for (const node of this.state.nodes) {
507
- if (node.role !== 'coordinator' && !node.connections.includes(coordinator.agentId)) {
508
- node.connections = [coordinator.agentId];
509
- this.adjacencyList.set(node.agentId, new Set([coordinator.agentId]));
510
- coordinator.connections.push(node.agentId);
511
- this.adjacencyList.get(coordinator.agentId)?.add(node.agentId);
512
- }
513
- }
514
- }
515
-
516
- private async rebalanceHybrid(): Promise<void> {
517
- // Hybrid combines mesh for workers and hierarchical for coordinators
518
- const coordinators = this.state.nodes.filter(
519
- n => n.role === 'queen' || n.role === 'coordinator'
520
- );
521
- const workers = this.state.nodes.filter(n => n.role === 'worker' || n.role === 'peer');
522
-
523
- // Connect workers in mesh (limited connections)
524
- for (const worker of workers) {
525
- const targetConnections = Math.min(3, workers.length - 1);
526
- const currentWorkerConnections = worker.connections.filter(
527
- c => workers.some(w => w.agentId === c)
528
- );
529
-
530
- while (currentWorkerConnections.length < targetConnections) {
531
- const candidates = workers.filter(
532
- w => w.agentId !== worker.agentId && !currentWorkerConnections.includes(w.agentId)
533
- );
534
- if (candidates.length === 0) break;
535
-
536
- const target = candidates[Math.floor(Math.random() * candidates.length)];
537
- worker.connections.push(target.agentId);
538
- currentWorkerConnections.push(target.agentId);
539
- this.adjacencyList.get(worker.agentId)?.add(target.agentId);
540
- }
541
- }
542
-
543
- // Connect all workers to at least one coordinator
544
- if (coordinators.length > 0) {
545
- for (const worker of workers) {
546
- const hasCoordinator = worker.connections.some(
547
- c => coordinators.some(coord => coord.agentId === c)
548
- );
549
- if (!hasCoordinator) {
550
- const coord = coordinators[Math.floor(Math.random() * coordinators.length)];
551
- worker.connections.push(coord.agentId);
552
- this.adjacencyList.get(worker.agentId)?.add(coord.agentId);
553
- coord.connections.push(worker.agentId);
554
- this.adjacencyList.get(coord.agentId)?.add(worker.agentId);
555
- }
556
- }
557
- }
558
- }
559
-
560
- // ===== ROLE INDEX METHODS (O(1) lookups) =====
561
-
562
- /**
563
- * Add node to role index
564
- */
565
- private addToRoleIndex(node: TopologyNode): void {
566
- let roleSet = this.roleIndex.get(node.role);
567
- if (!roleSet) {
568
- roleSet = new Set();
569
- this.roleIndex.set(node.role, roleSet);
570
- }
571
- roleSet.add(node.agentId);
572
-
573
- // Cache queen/coordinator for O(1) access
574
- if (node.role === 'queen') {
575
- this.queenNode = node;
576
- } else if (node.role === 'coordinator') {
577
- this.coordinatorNode = node;
578
- }
579
- }
580
-
581
- /**
582
- * Remove node from role index
583
- */
584
- private removeFromRoleIndex(node: TopologyNode): void {
585
- const roleSet = this.roleIndex.get(node.role);
586
- if (roleSet) {
587
- roleSet.delete(node.agentId);
588
- }
589
-
590
- // Clear cached queen/coordinator
591
- if (node.role === 'queen' && this.queenNode?.agentId === node.agentId) {
592
- this.queenNode = null;
593
- } else if (node.role === 'coordinator' && this.coordinatorNode?.agentId === node.agentId) {
594
- this.coordinatorNode = null;
595
- }
596
- }
597
-
598
- /**
599
- * Get queen node with O(1) lookup
600
- */
601
- getQueen(): TopologyNode | undefined {
602
- return this.queenNode ?? undefined;
603
- }
604
-
605
- /**
606
- * Get coordinator node with O(1) lookup
607
- */
608
- getCoordinator(): TopologyNode | undefined {
609
- return this.coordinatorNode ?? undefined;
610
- }
611
-
612
- // ===== UTILITY METHODS =====
613
-
614
- getNode(agentId: string): TopologyNode | undefined {
615
- return this.nodeIndex.get(agentId);
616
- }
617
-
618
- getNodesByRole(role: TopologyNode['role']): TopologyNode[] {
619
- // Use role index for O(1) id lookup, then O(k) node retrieval where k = nodes with role
620
- const roleSet = this.roleIndex.get(role);
621
- if (!roleSet) return [];
622
-
623
- const nodes: TopologyNode[] = [];
624
- for (const agentId of roleSet) {
625
- const node = this.nodeIndex.get(agentId);
626
- if (node) nodes.push(node);
627
- }
628
- return nodes;
629
- }
630
-
631
- getActiveNodes(): TopologyNode[] {
632
- return this.state.nodes.filter(n => n.status === 'active');
633
- }
634
-
635
- getPartition(partitionId: string): TopologyPartition | undefined {
636
- return this.state.partitions.find(p => p.id === partitionId);
637
- }
638
-
639
- isConnected(from: string, to: string): boolean {
640
- return this.adjacencyList.get(from)?.has(to) ?? false;
641
- }
642
-
643
- getConnectionCount(): number {
644
- return this.state.edges.length;
645
- }
646
-
647
- getAverageConnections(): number {
648
- if (this.state.nodes.length === 0) return 0;
649
- const total = this.state.nodes.reduce((sum, n) => sum + n.connections.length, 0);
650
- return total / this.state.nodes.length;
651
- }
652
- }
653
-
654
- export function createTopologyManager(config?: Partial<TopologyConfig>): TopologyManager {
655
- return new TopologyManager(config);
656
- }