@sparkleideas/claims 3.0.0-alpha.8-patch.26 → 3.0.0-alpha.8-patch.28
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/dist/api/cli-commands.d.ts +84 -0
- package/dist/api/cli-commands.d.ts.map +1 -0
- package/dist/api/cli-commands.js +1223 -0
- package/dist/api/cli-commands.js.map +1 -0
- package/dist/api/cli-types.d.ts +82 -0
- package/dist/api/cli-types.d.ts.map +1 -0
- package/dist/api/cli-types.js +87 -0
- package/dist/api/cli-types.js.map +1 -0
- package/dist/api/index.d.ts +9 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +11 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/mcp-tools.d.ts +376 -0
- package/dist/api/mcp-tools.d.ts.map +1 -0
- package/dist/api/mcp-tools.js +1409 -0
- package/dist/api/mcp-tools.js.map +1 -0
- package/dist/application/claim-service.d.ts +99 -0
- package/dist/application/claim-service.d.ts.map +1 -0
- package/dist/application/claim-service.js +440 -0
- package/dist/application/claim-service.js.map +1 -0
- package/dist/application/index.d.ts +15 -0
- package/dist/application/index.d.ts.map +1 -0
- package/dist/application/index.js +17 -0
- package/dist/application/index.js.map +1 -0
- package/dist/application/load-balancer.d.ts +353 -0
- package/dist/application/load-balancer.d.ts.map +1 -0
- package/dist/application/load-balancer.js +430 -0
- package/dist/application/load-balancer.js.map +1 -0
- package/dist/application/work-stealing-service.d.ts +149 -0
- package/dist/application/work-stealing-service.d.ts.map +1 -0
- package/dist/application/work-stealing-service.js +604 -0
- package/dist/application/work-stealing-service.js.map +1 -0
- package/dist/domain/events.d.ts +308 -0
- package/dist/domain/events.d.ts.map +1 -0
- package/dist/domain/events.js +241 -0
- package/dist/domain/events.js.map +1 -0
- package/dist/domain/index.d.ts +16 -0
- package/dist/domain/index.d.ts.map +1 -0
- package/dist/domain/index.js +34 -0
- package/dist/domain/index.js.map +1 -0
- package/dist/domain/repositories.d.ts +154 -0
- package/dist/domain/repositories.d.ts.map +1 -0
- package/dist/domain/repositories.js +9 -0
- package/dist/domain/repositories.js.map +1 -0
- package/dist/domain/rules.d.ts +105 -0
- package/dist/domain/rules.d.ts.map +1 -0
- package/dist/domain/rules.js +348 -0
- package/dist/domain/rules.js.map +1 -0
- package/dist/domain/types.d.ts +578 -0
- package/dist/domain/types.d.ts.map +1 -0
- package/dist/domain/types.js +99 -0
- package/dist/domain/types.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure/claim-repository.d.ts +46 -0
- package/dist/infrastructure/claim-repository.d.ts.map +1 -0
- package/dist/infrastructure/claim-repository.js +274 -0
- package/dist/infrastructure/claim-repository.js.map +1 -0
- package/dist/infrastructure/event-store.d.ts +62 -0
- package/dist/infrastructure/event-store.d.ts.map +1 -0
- package/dist/infrastructure/event-store.js +189 -0
- package/dist/infrastructure/event-store.js.map +1 -0
- package/dist/infrastructure/index.d.ts +10 -0
- package/dist/infrastructure/index.d.ts.map +1 -0
- package/dist/infrastructure/index.js +12 -0
- package/dist/infrastructure/index.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load Balancer Service for Claims Module (ADR-016)
|
|
3
|
+
*
|
|
4
|
+
* Balances work across the swarm by:
|
|
5
|
+
* - Tracking agent load and utilization
|
|
6
|
+
* - Detecting overloaded/underloaded agents
|
|
7
|
+
* - Rebalancing work through handoff mechanisms
|
|
8
|
+
*
|
|
9
|
+
* Rebalancing Algorithm:
|
|
10
|
+
* 1. Calculate average load across swarm
|
|
11
|
+
* 2. Identify overloaded agents (>1.5x average utilization)
|
|
12
|
+
* 3. Identify underloaded agents (<0.5x average utilization)
|
|
13
|
+
* 4. Move low-progress (<25%) work from overloaded to underloaded
|
|
14
|
+
* 5. Prefer same agent type for transfers
|
|
15
|
+
* 6. Use handoff mechanism (not direct reassignment)
|
|
16
|
+
*
|
|
17
|
+
* Events Emitted:
|
|
18
|
+
* - SwarmRebalanced: When rebalancing operation completes
|
|
19
|
+
* - AgentOverloaded: When an agent exceeds load threshold
|
|
20
|
+
* - AgentUnderloaded: When an agent is below load threshold
|
|
21
|
+
*
|
|
22
|
+
* @module v3/@sparkleideas/claims/application/load-balancer
|
|
23
|
+
*/
|
|
24
|
+
import { EventEmitter } from 'node:events';
|
|
25
|
+
// =============================================================================
|
|
26
|
+
// Load Balancer Implementation
|
|
27
|
+
// =============================================================================
|
|
28
|
+
const DEFAULT_REBALANCE_OPTIONS = {
|
|
29
|
+
maxProgressToMove: 25,
|
|
30
|
+
preferSameType: true,
|
|
31
|
+
overloadThreshold: 1.5,
|
|
32
|
+
underloadThreshold: 0.5,
|
|
33
|
+
maxMovesPerRebalance: 10,
|
|
34
|
+
useHandoff: true,
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Load Balancer Service
|
|
38
|
+
*
|
|
39
|
+
* Balances work across the swarm using the following algorithm:
|
|
40
|
+
* 1. Calculate average load across swarm
|
|
41
|
+
* 2. Identify overloaded agents (>1.5x average utilization)
|
|
42
|
+
* 3. Identify underloaded agents (<0.5x average utilization)
|
|
43
|
+
* 4. Move low-progress (<25%) work from overloaded to underloaded
|
|
44
|
+
* 5. Prefer same agent type for transfers
|
|
45
|
+
* 6. Use handoff mechanism (not direct reassignment)
|
|
46
|
+
*/
|
|
47
|
+
export class LoadBalancer extends EventEmitter {
|
|
48
|
+
claimRepository;
|
|
49
|
+
agentRegistry;
|
|
50
|
+
handoffService;
|
|
51
|
+
constructor(claimRepository, agentRegistry, handoffService) {
|
|
52
|
+
super();
|
|
53
|
+
this.claimRepository = claimRepository;
|
|
54
|
+
this.agentRegistry = agentRegistry;
|
|
55
|
+
this.handoffService = handoffService;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Get load information for a specific agent
|
|
59
|
+
*/
|
|
60
|
+
async getAgentLoad(agentId) {
|
|
61
|
+
const agent = await this.agentRegistry.getAgent(agentId);
|
|
62
|
+
if (!agent) {
|
|
63
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
64
|
+
}
|
|
65
|
+
const claims = await this.claimRepository.getClaimsByAgent(agentId);
|
|
66
|
+
const completionHistory = await this.claimRepository.getAgentCompletionHistory(agentId, 50);
|
|
67
|
+
const utilization = this.calculateUtilization(claims, agent.maxClaims);
|
|
68
|
+
const blockedCount = claims.filter((c) => c.status === 'blocked').length;
|
|
69
|
+
const avgCompletionTime = completionHistory.length > 0
|
|
70
|
+
? completionHistory.reduce((sum, t) => sum + t, 0) / completionHistory.length
|
|
71
|
+
: 0;
|
|
72
|
+
return {
|
|
73
|
+
agentId: agent.agentId,
|
|
74
|
+
agentType: agent.agentType,
|
|
75
|
+
claimCount: claims.length,
|
|
76
|
+
maxClaims: agent.maxClaims,
|
|
77
|
+
utilization,
|
|
78
|
+
claims,
|
|
79
|
+
avgCompletionTime,
|
|
80
|
+
currentBlockedCount: blockedCount,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get load overview for entire swarm
|
|
85
|
+
*/
|
|
86
|
+
async getSwarmLoad(swarmId) {
|
|
87
|
+
const agents = await this.agentRegistry.getAgentsBySwarm(swarmId);
|
|
88
|
+
const claimsByAgent = await this.claimRepository.getClaimsBySwarm(swarmId);
|
|
89
|
+
const agentLoads = [];
|
|
90
|
+
let totalClaims = 0;
|
|
91
|
+
let totalUtilization = 0;
|
|
92
|
+
let activeAgents = 0;
|
|
93
|
+
for (const agent of agents) {
|
|
94
|
+
const claims = claimsByAgent.get(agent.agentId) || [];
|
|
95
|
+
const completionHistory = await this.claimRepository.getAgentCompletionHistory(agent.agentId, 50);
|
|
96
|
+
const utilization = this.calculateUtilization(claims, agent.maxClaims);
|
|
97
|
+
const blockedCount = claims.filter((c) => c.status === 'blocked').length;
|
|
98
|
+
const avgCompletionTime = completionHistory.length > 0
|
|
99
|
+
? completionHistory.reduce((sum, t) => sum + t, 0) / completionHistory.length
|
|
100
|
+
: 0;
|
|
101
|
+
const loadInfo = {
|
|
102
|
+
agentId: agent.agentId,
|
|
103
|
+
agentType: agent.agentType,
|
|
104
|
+
claimCount: claims.length,
|
|
105
|
+
maxClaims: agent.maxClaims,
|
|
106
|
+
utilization,
|
|
107
|
+
claims,
|
|
108
|
+
avgCompletionTime,
|
|
109
|
+
currentBlockedCount: blockedCount,
|
|
110
|
+
};
|
|
111
|
+
agentLoads.push(loadInfo);
|
|
112
|
+
totalClaims += claims.length;
|
|
113
|
+
totalUtilization += utilization;
|
|
114
|
+
if (claims.length > 0) {
|
|
115
|
+
activeAgents++;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const avgUtilization = agents.length > 0 ? totalUtilization / agents.length : 0;
|
|
119
|
+
// Detect overloaded/underloaded
|
|
120
|
+
const overloadedAgents = agentLoads
|
|
121
|
+
.filter((a) => a.utilization > avgUtilization * DEFAULT_REBALANCE_OPTIONS.overloadThreshold)
|
|
122
|
+
.map((a) => a.agentId);
|
|
123
|
+
const underloadedAgents = agentLoads
|
|
124
|
+
.filter((a) => a.utilization < avgUtilization * DEFAULT_REBALANCE_OPTIONS.underloadThreshold)
|
|
125
|
+
.map((a) => a.agentId);
|
|
126
|
+
// Calculate balance score (0-1, higher is better)
|
|
127
|
+
const balanceScore = this.calculateBalanceScore(agentLoads);
|
|
128
|
+
return {
|
|
129
|
+
swarmId,
|
|
130
|
+
totalAgents: agents.length,
|
|
131
|
+
activeAgents,
|
|
132
|
+
totalClaims,
|
|
133
|
+
avgUtilization,
|
|
134
|
+
agents: agentLoads,
|
|
135
|
+
overloadedAgents,
|
|
136
|
+
underloadedAgents,
|
|
137
|
+
balanceScore,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Rebalance work across swarm
|
|
142
|
+
*/
|
|
143
|
+
async rebalance(swarmId, options) {
|
|
144
|
+
const startTime = Date.now();
|
|
145
|
+
const opts = { ...DEFAULT_REBALANCE_OPTIONS, ...options };
|
|
146
|
+
// Get current state
|
|
147
|
+
const swarmLoad = await this.getSwarmLoad(swarmId);
|
|
148
|
+
const previousBalanceScore = swarmLoad.balanceScore;
|
|
149
|
+
// Detect imbalance
|
|
150
|
+
const imbalance = await this.detectImbalance(swarmId);
|
|
151
|
+
const moved = [];
|
|
152
|
+
const suggested = [];
|
|
153
|
+
// Process overloaded agents
|
|
154
|
+
for (const overloaded of imbalance.overloaded) {
|
|
155
|
+
// Find movable claims (low progress, not blocked)
|
|
156
|
+
const movableClaims = overloaded.movableClaims
|
|
157
|
+
.filter((c) => c.progress < opts.maxProgressToMove && c.status === 'active')
|
|
158
|
+
.sort((a, b) => a.progress - b.progress); // Move lowest progress first
|
|
159
|
+
for (const claim of movableClaims) {
|
|
160
|
+
if (moved.length + suggested.length >= opts.maxMovesPerRebalance) {
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
// Find suitable target agent
|
|
164
|
+
const target = this.findBestTarget(overloaded.agentType, imbalance.underloaded, opts.preferSameType);
|
|
165
|
+
if (!target) {
|
|
166
|
+
suggested.push({
|
|
167
|
+
issueId: claim.issueId,
|
|
168
|
+
currentOwner: { type: 'agent', agentId: overloaded.agentId, agentType: overloaded.agentType },
|
|
169
|
+
suggestedOwner: { type: 'agent', agentId: 'none-available', agentType: overloaded.agentType },
|
|
170
|
+
reason: 'No suitable underloaded agent available',
|
|
171
|
+
});
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
const from = {
|
|
175
|
+
type: 'agent',
|
|
176
|
+
agentId: overloaded.agentId,
|
|
177
|
+
agentType: overloaded.agentType,
|
|
178
|
+
};
|
|
179
|
+
const to = {
|
|
180
|
+
type: 'agent',
|
|
181
|
+
agentId: target.agentId,
|
|
182
|
+
agentType: target.agentType,
|
|
183
|
+
};
|
|
184
|
+
if (opts.useHandoff) {
|
|
185
|
+
// Use handoff mechanism
|
|
186
|
+
await this.handoffService.requestHandoff(claim.issueId, from, to, `Load balancing: redistributing work across swarm (${overloaded.utilization.toFixed(2)} -> ${target.utilization.toFixed(2)} utilization)`);
|
|
187
|
+
moved.push({ issueId: claim.issueId, from, to });
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
// Just suggest, don't execute
|
|
191
|
+
suggested.push({
|
|
192
|
+
issueId: claim.issueId,
|
|
193
|
+
currentOwner: from,
|
|
194
|
+
suggestedOwner: to,
|
|
195
|
+
reason: `Load balancing: agent ${overloaded.agentId} overloaded at ${(overloaded.utilization * 100).toFixed(0)}%`,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
// Update target's capacity tracking (in-memory for this operation)
|
|
199
|
+
target.availableCapacity--;
|
|
200
|
+
if (target.availableCapacity <= 0) {
|
|
201
|
+
const idx = imbalance.underloaded.indexOf(target);
|
|
202
|
+
if (idx >= 0) {
|
|
203
|
+
imbalance.underloaded.splice(idx, 1);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// Calculate new balance score
|
|
209
|
+
const newSwarmLoad = await this.getSwarmLoad(swarmId);
|
|
210
|
+
const newBalanceScore = newSwarmLoad.balanceScore;
|
|
211
|
+
const result = {
|
|
212
|
+
moved,
|
|
213
|
+
suggested,
|
|
214
|
+
stats: {
|
|
215
|
+
totalMoved: moved.length,
|
|
216
|
+
totalSuggested: suggested.length,
|
|
217
|
+
previousBalanceScore,
|
|
218
|
+
newBalanceScore,
|
|
219
|
+
executionTimeMs: Date.now() - startTime,
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
// Emit swarm rebalanced event
|
|
223
|
+
this.emit('swarm:rebalanced', {
|
|
224
|
+
type: 'swarm:rebalanced',
|
|
225
|
+
swarmId,
|
|
226
|
+
timestamp: new Date(),
|
|
227
|
+
result,
|
|
228
|
+
});
|
|
229
|
+
return result;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Preview rebalance without applying changes
|
|
233
|
+
*/
|
|
234
|
+
async previewRebalance(swarmId, options) {
|
|
235
|
+
// Force useHandoff to false for preview - we just want suggestions
|
|
236
|
+
return this.rebalance(swarmId, { ...options, useHandoff: false });
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Detect overloaded/underloaded agents
|
|
240
|
+
*/
|
|
241
|
+
async detectImbalance(swarmId) {
|
|
242
|
+
const swarmLoad = await this.getSwarmLoad(swarmId);
|
|
243
|
+
const avgLoad = swarmLoad.avgUtilization;
|
|
244
|
+
const overloaded = [];
|
|
245
|
+
const underloaded = [];
|
|
246
|
+
const recommendations = [];
|
|
247
|
+
for (const agent of swarmLoad.agents) {
|
|
248
|
+
const isOverloaded = agent.utilization > avgLoad * DEFAULT_REBALANCE_OPTIONS.overloadThreshold;
|
|
249
|
+
const isUnderloaded = agent.utilization < avgLoad * DEFAULT_REBALANCE_OPTIONS.underloadThreshold;
|
|
250
|
+
if (isOverloaded) {
|
|
251
|
+
const excessClaims = Math.ceil(agent.claimCount - agent.maxClaims * avgLoad);
|
|
252
|
+
const movableClaims = agent.claims.filter((c) => c.progress < DEFAULT_REBALANCE_OPTIONS.maxProgressToMove);
|
|
253
|
+
overloaded.push({
|
|
254
|
+
agentId: agent.agentId,
|
|
255
|
+
agentType: agent.agentType,
|
|
256
|
+
utilization: agent.utilization,
|
|
257
|
+
excessClaims: Math.max(0, excessClaims),
|
|
258
|
+
movableClaims,
|
|
259
|
+
});
|
|
260
|
+
// Emit overloaded event
|
|
261
|
+
this.emit('agent:overloaded', {
|
|
262
|
+
type: 'agent:overloaded',
|
|
263
|
+
agentId: agent.agentId,
|
|
264
|
+
agentType: agent.agentType,
|
|
265
|
+
utilization: agent.utilization,
|
|
266
|
+
claimCount: agent.claimCount,
|
|
267
|
+
maxClaims: agent.maxClaims,
|
|
268
|
+
timestamp: new Date(),
|
|
269
|
+
});
|
|
270
|
+
if (movableClaims.length > 0) {
|
|
271
|
+
recommendations.push(`Agent ${agent.agentId} (${agent.agentType}) is overloaded at ${(agent.utilization * 100).toFixed(0)}% with ${movableClaims.length} movable claims`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (isUnderloaded) {
|
|
275
|
+
const availableCapacity = agent.maxClaims - agent.claimCount;
|
|
276
|
+
underloaded.push({
|
|
277
|
+
agentId: agent.agentId,
|
|
278
|
+
agentType: agent.agentType,
|
|
279
|
+
utilization: agent.utilization,
|
|
280
|
+
availableCapacity,
|
|
281
|
+
});
|
|
282
|
+
// Emit underloaded event
|
|
283
|
+
this.emit('agent:underloaded', {
|
|
284
|
+
type: 'agent:underloaded',
|
|
285
|
+
agentId: agent.agentId,
|
|
286
|
+
agentType: agent.agentType,
|
|
287
|
+
utilization: agent.utilization,
|
|
288
|
+
claimCount: agent.claimCount,
|
|
289
|
+
maxClaims: agent.maxClaims,
|
|
290
|
+
timestamp: new Date(),
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// Generate recommendations
|
|
295
|
+
if (overloaded.length > 0 && underloaded.length > 0) {
|
|
296
|
+
const totalMovable = overloaded.reduce((sum, o) => sum + o.movableClaims.length, 0);
|
|
297
|
+
const totalCapacity = underloaded.reduce((sum, u) => sum + u.availableCapacity, 0);
|
|
298
|
+
recommendations.push(`Can redistribute up to ${Math.min(totalMovable, totalCapacity)} claims from ${overloaded.length} overloaded to ${underloaded.length} underloaded agents`);
|
|
299
|
+
}
|
|
300
|
+
if (overloaded.length > 0 && underloaded.length === 0) {
|
|
301
|
+
recommendations.push(`${overloaded.length} overloaded agents but no underloaded agents available. Consider spawning more agents.`);
|
|
302
|
+
}
|
|
303
|
+
const isBalanced = overloaded.length === 0 ||
|
|
304
|
+
swarmLoad.balanceScore > 0.8;
|
|
305
|
+
return {
|
|
306
|
+
swarmId,
|
|
307
|
+
timestamp: new Date(),
|
|
308
|
+
isBalanced,
|
|
309
|
+
balanceScore: swarmLoad.balanceScore,
|
|
310
|
+
avgLoad,
|
|
311
|
+
overloaded,
|
|
312
|
+
underloaded,
|
|
313
|
+
recommendations,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
// =============================================================================
|
|
317
|
+
// Private Helper Methods
|
|
318
|
+
// =============================================================================
|
|
319
|
+
/**
|
|
320
|
+
* Calculate utilization based on claims and their priorities
|
|
321
|
+
*/
|
|
322
|
+
calculateUtilization(claims, maxClaims) {
|
|
323
|
+
if (maxClaims === 0)
|
|
324
|
+
return 0;
|
|
325
|
+
if (claims.length === 0)
|
|
326
|
+
return 0;
|
|
327
|
+
// Weight claims by priority
|
|
328
|
+
const priorityWeights = {
|
|
329
|
+
critical: 2.0,
|
|
330
|
+
high: 1.5,
|
|
331
|
+
medium: 1.0,
|
|
332
|
+
low: 0.5,
|
|
333
|
+
};
|
|
334
|
+
let weightedCount = 0;
|
|
335
|
+
for (const claim of claims) {
|
|
336
|
+
const weight = priorityWeights[claim.priority] || 1.0;
|
|
337
|
+
// Blocked claims count less toward utilization
|
|
338
|
+
const blockFactor = claim.status === 'blocked' ? 0.5 : 1.0;
|
|
339
|
+
weightedCount += weight * blockFactor;
|
|
340
|
+
}
|
|
341
|
+
// Normalize to 0-1 range
|
|
342
|
+
return Math.min(1, weightedCount / maxClaims);
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Calculate balance score for the swarm (0-1, higher is better)
|
|
346
|
+
*
|
|
347
|
+
* Uses coefficient of variation: 1 - (stdDev / mean)
|
|
348
|
+
* A perfectly balanced swarm has score = 1
|
|
349
|
+
*/
|
|
350
|
+
calculateBalanceScore(agentLoads) {
|
|
351
|
+
if (agentLoads.length === 0)
|
|
352
|
+
return 1;
|
|
353
|
+
if (agentLoads.length === 1)
|
|
354
|
+
return 1;
|
|
355
|
+
const utilizations = agentLoads.map((a) => a.utilization);
|
|
356
|
+
const mean = utilizations.reduce((sum, u) => sum + u, 0) / utilizations.length;
|
|
357
|
+
if (mean === 0)
|
|
358
|
+
return 1; // No work = perfectly balanced
|
|
359
|
+
const variance = utilizations.reduce((sum, u) => sum + Math.pow(u - mean, 2), 0) /
|
|
360
|
+
utilizations.length;
|
|
361
|
+
const stdDev = Math.sqrt(variance);
|
|
362
|
+
// Coefficient of variation normalized to 0-1
|
|
363
|
+
const cv = stdDev / mean;
|
|
364
|
+
const score = Math.max(0, Math.min(1, 1 - cv));
|
|
365
|
+
return score;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Find the best target agent for receiving a transferred claim
|
|
369
|
+
*/
|
|
370
|
+
findBestTarget(sourceAgentType, candidates, preferSameType) {
|
|
371
|
+
if (candidates.length === 0)
|
|
372
|
+
return null;
|
|
373
|
+
// Sort candidates by preference
|
|
374
|
+
const sorted = [...candidates].sort((a, b) => {
|
|
375
|
+
// Prefer same type if configured
|
|
376
|
+
if (preferSameType) {
|
|
377
|
+
const aMatch = a.agentType === sourceAgentType ? 0 : 1;
|
|
378
|
+
const bMatch = b.agentType === sourceAgentType ? 0 : 1;
|
|
379
|
+
if (aMatch !== bMatch)
|
|
380
|
+
return aMatch - bMatch;
|
|
381
|
+
}
|
|
382
|
+
// Then by available capacity (more is better)
|
|
383
|
+
if (a.availableCapacity !== b.availableCapacity) {
|
|
384
|
+
return b.availableCapacity - a.availableCapacity;
|
|
385
|
+
}
|
|
386
|
+
// Then by utilization (lower is better)
|
|
387
|
+
return a.utilization - b.utilization;
|
|
388
|
+
});
|
|
389
|
+
return sorted[0] || null;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
// =============================================================================
|
|
393
|
+
// Factory Function
|
|
394
|
+
// =============================================================================
|
|
395
|
+
/**
|
|
396
|
+
* Create a LoadBalancer instance with dependencies
|
|
397
|
+
*
|
|
398
|
+
* @param claimRepository - Repository for accessing claim data
|
|
399
|
+
* @param agentRegistry - Registry for agent metadata
|
|
400
|
+
* @param handoffService - Service for initiating claim handoffs
|
|
401
|
+
* @returns A configured LoadBalancer instance
|
|
402
|
+
*
|
|
403
|
+
* @example
|
|
404
|
+
* ```typescript
|
|
405
|
+
* const loadBalancer = createLoadBalancer(
|
|
406
|
+
* claimRepository,
|
|
407
|
+
* agentRegistry,
|
|
408
|
+
* handoffService
|
|
409
|
+
* );
|
|
410
|
+
*
|
|
411
|
+
* // Get swarm load overview
|
|
412
|
+
* const swarmLoad = await loadBalancer.getSwarmLoad('swarm-1');
|
|
413
|
+
*
|
|
414
|
+
* // Detect and report imbalances
|
|
415
|
+
* const imbalance = await loadBalancer.detectImbalance('swarm-1');
|
|
416
|
+
*
|
|
417
|
+
* // Preview rebalancing without applying
|
|
418
|
+
* const preview = await loadBalancer.previewRebalance('swarm-1');
|
|
419
|
+
*
|
|
420
|
+
* // Execute rebalancing with handoffs
|
|
421
|
+
* const result = await loadBalancer.rebalance('swarm-1', {
|
|
422
|
+
* maxProgressToMove: 25,
|
|
423
|
+
* preferSameType: true
|
|
424
|
+
* });
|
|
425
|
+
* ```
|
|
426
|
+
*/
|
|
427
|
+
export function createLoadBalancer(claimRepository, agentRegistry, handoffService) {
|
|
428
|
+
return new LoadBalancer(claimRepository, agentRegistry, handoffService);
|
|
429
|
+
}
|
|
430
|
+
//# sourceMappingURL=load-balancer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-balancer.js","sourceRoot":"","sources":["../../src/application/load-balancer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Work Stealing Service - Application Layer
|
|
3
|
+
*
|
|
4
|
+
* Handles work stealing to maximize swarm throughput by redistributing
|
|
5
|
+
* work from stale, blocked, or overloaded agents to available ones.
|
|
6
|
+
*
|
|
7
|
+
* @module v3/claims/application/work-stealing-service
|
|
8
|
+
*/
|
|
9
|
+
import { type IssueId, type Claimant, type AgentType, type StealableInfo, type StealResult, type WorkStealingConfig, type IssueClaimWithStealing, type IIssueClaimRepository, type IWorkStealingEventBus, type WorkStealingEvent, type WorkStealingEventType } from '../domain/types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Work Stealing Service Interface
|
|
12
|
+
*/
|
|
13
|
+
export interface IWorkStealingService {
|
|
14
|
+
/** Mark work as stealable */
|
|
15
|
+
markStealable(issueId: IssueId, info: StealableInfo): Promise<void>;
|
|
16
|
+
/** Steal work from another agent */
|
|
17
|
+
steal(issueId: IssueId, stealer: Claimant): Promise<StealResult>;
|
|
18
|
+
/** Get list of stealable issues */
|
|
19
|
+
getStealable(agentType?: AgentType): Promise<IssueClaimWithStealing[]>;
|
|
20
|
+
/** Contest a steal (original owner wants it back) */
|
|
21
|
+
contestSteal(issueId: IssueId, originalClaimant: Claimant, reason: string): Promise<void>;
|
|
22
|
+
/** Resolve contest (queen/human decides) */
|
|
23
|
+
resolveContest(issueId: IssueId, winner: Claimant, reason: string): Promise<void>;
|
|
24
|
+
/** Auto-detect stealable work based on config thresholds */
|
|
25
|
+
detectStaleWork(config: WorkStealingConfig): Promise<IssueClaimWithStealing[]>;
|
|
26
|
+
/** Auto-mark stealable work based on config thresholds */
|
|
27
|
+
autoMarkStealable(config: WorkStealingConfig): Promise<number>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Simple in-memory event bus for work stealing events
|
|
31
|
+
*/
|
|
32
|
+
export declare class InMemoryWorkStealingEventBus implements IWorkStealingEventBus {
|
|
33
|
+
private handlers;
|
|
34
|
+
private history;
|
|
35
|
+
private maxHistorySize;
|
|
36
|
+
constructor(options?: {
|
|
37
|
+
maxHistorySize?: number;
|
|
38
|
+
});
|
|
39
|
+
emit(event: WorkStealingEvent): Promise<void>;
|
|
40
|
+
subscribe(eventType: WorkStealingEventType, handler: (event: WorkStealingEvent) => void | Promise<void>): () => void;
|
|
41
|
+
subscribeAll(handler: (event: WorkStealingEvent) => void | Promise<void>): () => void;
|
|
42
|
+
getHistory(filter?: {
|
|
43
|
+
types?: WorkStealingEventType[];
|
|
44
|
+
limit?: number;
|
|
45
|
+
}): WorkStealingEvent[];
|
|
46
|
+
private addToHistory;
|
|
47
|
+
private safeExecute;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Work Stealing Service
|
|
51
|
+
*
|
|
52
|
+
* Implements work stealing algorithms to maximize swarm throughput by
|
|
53
|
+
* redistributing work from stale, blocked, or overloaded agents.
|
|
54
|
+
*/
|
|
55
|
+
export declare class WorkStealingService implements IWorkStealingService {
|
|
56
|
+
private readonly repository;
|
|
57
|
+
private readonly eventBus;
|
|
58
|
+
private readonly config;
|
|
59
|
+
constructor(repository: IIssueClaimRepository, eventBus: IWorkStealingEventBus, config?: Partial<WorkStealingConfig>);
|
|
60
|
+
/**
|
|
61
|
+
* Mark work as stealable with the given reason
|
|
62
|
+
*/
|
|
63
|
+
markStealable(issueId: IssueId, info: StealableInfo): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Steal work from another agent
|
|
66
|
+
*/
|
|
67
|
+
steal(issueId: IssueId, stealer: Claimant): Promise<StealResult>;
|
|
68
|
+
/**
|
|
69
|
+
* Get list of stealable issues, optionally filtered by agent type
|
|
70
|
+
*/
|
|
71
|
+
getStealable(agentType?: AgentType): Promise<IssueClaimWithStealing[]>;
|
|
72
|
+
/**
|
|
73
|
+
* Contest a steal (original owner wants the work back)
|
|
74
|
+
*/
|
|
75
|
+
contestSteal(issueId: IssueId, originalClaimant: Claimant, reason: string): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Resolve a contest (queen or human decides the winner)
|
|
78
|
+
*/
|
|
79
|
+
resolveContest(issueId: IssueId, winner: Claimant, reason: string): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Detect stale work based on config thresholds
|
|
82
|
+
*/
|
|
83
|
+
detectStaleWork(config: WorkStealingConfig): Promise<IssueClaimWithStealing[]>;
|
|
84
|
+
/**
|
|
85
|
+
* Auto-mark stealable work based on config thresholds
|
|
86
|
+
*/
|
|
87
|
+
autoMarkStealable(config: WorkStealingConfig): Promise<number>;
|
|
88
|
+
/**
|
|
89
|
+
* Check if claim is in grace period
|
|
90
|
+
*/
|
|
91
|
+
private isInGracePeriod;
|
|
92
|
+
/**
|
|
93
|
+
* Check if claim is in grace period with specific config
|
|
94
|
+
*/
|
|
95
|
+
private isInGracePeriodWithConfig;
|
|
96
|
+
/**
|
|
97
|
+
* Check if claim is protected by progress
|
|
98
|
+
*/
|
|
99
|
+
private isProtectedByProgress;
|
|
100
|
+
/**
|
|
101
|
+
* Check if claim is protected by progress with specific config
|
|
102
|
+
*/
|
|
103
|
+
private isProtectedByProgressWithConfig;
|
|
104
|
+
/**
|
|
105
|
+
* Get agent type from claimant
|
|
106
|
+
*/
|
|
107
|
+
private getAgentType;
|
|
108
|
+
/**
|
|
109
|
+
* Check if cross-type stealing is allowed
|
|
110
|
+
*/
|
|
111
|
+
private canStealCrossType;
|
|
112
|
+
/**
|
|
113
|
+
* Get allowed stealer types for a claimant
|
|
114
|
+
*/
|
|
115
|
+
private getAllowedStealerTypes;
|
|
116
|
+
/**
|
|
117
|
+
* Determine the stale reason for a claim
|
|
118
|
+
*/
|
|
119
|
+
private determineStaleReason;
|
|
120
|
+
/**
|
|
121
|
+
* Determine who resolved the contest
|
|
122
|
+
*/
|
|
123
|
+
private determineResolver;
|
|
124
|
+
/**
|
|
125
|
+
* Create a steal error result
|
|
126
|
+
*/
|
|
127
|
+
private stealError;
|
|
128
|
+
/**
|
|
129
|
+
* Emit IssueMarkedStealable event
|
|
130
|
+
*/
|
|
131
|
+
private emitMarkedStealableEvent;
|
|
132
|
+
/**
|
|
133
|
+
* Emit IssueStolen event
|
|
134
|
+
*/
|
|
135
|
+
private emitStolenEvent;
|
|
136
|
+
/**
|
|
137
|
+
* Emit StealContested event
|
|
138
|
+
*/
|
|
139
|
+
private emitContestEvent;
|
|
140
|
+
/**
|
|
141
|
+
* Emit StealContestResolved event
|
|
142
|
+
*/
|
|
143
|
+
private emitContestResolvedEvent;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Create a new WorkStealingService with default event bus
|
|
147
|
+
*/
|
|
148
|
+
export declare function createWorkStealingService(repository: IIssueClaimRepository, config?: Partial<WorkStealingConfig>, eventBus?: IWorkStealingEventBus): WorkStealingService;
|
|
149
|
+
//# sourceMappingURL=work-stealing-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"work-stealing-service.d.ts","sourceRoot":"","sources":["../../src/application/work-stealing-service.ts"],"names":[],"mappings":"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"}
|