@sparkleideas/claims 3.0.0-alpha.8-patch.26 → 3.0.0-alpha.8-patch.27

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 (69) hide show
  1. package/dist/api/cli-commands.d.ts +84 -0
  2. package/dist/api/cli-commands.d.ts.map +1 -0
  3. package/dist/api/cli-commands.js +1223 -0
  4. package/dist/api/cli-commands.js.map +1 -0
  5. package/dist/api/cli-types.d.ts +82 -0
  6. package/dist/api/cli-types.d.ts.map +1 -0
  7. package/dist/api/cli-types.js +87 -0
  8. package/dist/api/cli-types.js.map +1 -0
  9. package/dist/api/index.d.ts +9 -0
  10. package/dist/api/index.d.ts.map +1 -0
  11. package/dist/api/index.js +11 -0
  12. package/dist/api/index.js.map +1 -0
  13. package/dist/api/mcp-tools.d.ts +376 -0
  14. package/dist/api/mcp-tools.d.ts.map +1 -0
  15. package/dist/api/mcp-tools.js +1409 -0
  16. package/dist/api/mcp-tools.js.map +1 -0
  17. package/dist/application/claim-service.d.ts +99 -0
  18. package/dist/application/claim-service.d.ts.map +1 -0
  19. package/dist/application/claim-service.js +440 -0
  20. package/dist/application/claim-service.js.map +1 -0
  21. package/dist/application/index.d.ts +15 -0
  22. package/dist/application/index.d.ts.map +1 -0
  23. package/dist/application/index.js +17 -0
  24. package/dist/application/index.js.map +1 -0
  25. package/dist/application/load-balancer.d.ts +353 -0
  26. package/dist/application/load-balancer.d.ts.map +1 -0
  27. package/dist/application/load-balancer.js +430 -0
  28. package/dist/application/load-balancer.js.map +1 -0
  29. package/dist/application/work-stealing-service.d.ts +149 -0
  30. package/dist/application/work-stealing-service.d.ts.map +1 -0
  31. package/dist/application/work-stealing-service.js +604 -0
  32. package/dist/application/work-stealing-service.js.map +1 -0
  33. package/dist/domain/events.d.ts +308 -0
  34. package/dist/domain/events.d.ts.map +1 -0
  35. package/dist/domain/events.js +241 -0
  36. package/dist/domain/events.js.map +1 -0
  37. package/dist/domain/index.d.ts +16 -0
  38. package/dist/domain/index.d.ts.map +1 -0
  39. package/dist/domain/index.js +34 -0
  40. package/dist/domain/index.js.map +1 -0
  41. package/dist/domain/repositories.d.ts +154 -0
  42. package/dist/domain/repositories.d.ts.map +1 -0
  43. package/dist/domain/repositories.js +9 -0
  44. package/dist/domain/repositories.js.map +1 -0
  45. package/dist/domain/rules.d.ts +105 -0
  46. package/dist/domain/rules.d.ts.map +1 -0
  47. package/dist/domain/rules.js +348 -0
  48. package/dist/domain/rules.js.map +1 -0
  49. package/dist/domain/types.d.ts +578 -0
  50. package/dist/domain/types.d.ts.map +1 -0
  51. package/dist/domain/types.js +99 -0
  52. package/dist/domain/types.js.map +1 -0
  53. package/dist/index.d.ts +33 -0
  54. package/dist/index.d.ts.map +1 -0
  55. package/dist/index.js +51 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/infrastructure/claim-repository.d.ts +46 -0
  58. package/dist/infrastructure/claim-repository.d.ts.map +1 -0
  59. package/dist/infrastructure/claim-repository.js +274 -0
  60. package/dist/infrastructure/claim-repository.js.map +1 -0
  61. package/dist/infrastructure/event-store.d.ts +62 -0
  62. package/dist/infrastructure/event-store.d.ts.map +1 -0
  63. package/dist/infrastructure/event-store.js +189 -0
  64. package/dist/infrastructure/event-store.js.map +1 -0
  65. package/dist/infrastructure/index.d.ts +10 -0
  66. package/dist/infrastructure/index.d.ts.map +1 -0
  67. package/dist/infrastructure/index.js +12 -0
  68. package/dist/infrastructure/index.js.map +1 -0
  69. package/package.json +2 -2
@@ -0,0 +1,1409 @@
1
+ /**
2
+ * V3 MCP Claims Tools
3
+ *
4
+ * MCP tools for issue claiming and work coordination:
5
+ *
6
+ * Core Claiming (7 tools):
7
+ * - claims/issue_claim - Claim an issue to work on
8
+ * - claims/issue_release - Release a claim
9
+ * - claims/issue_handoff - Request handoff to another agent/human
10
+ * - claims/issue_status_update - Update claim status
11
+ * - claims/issue_list_available - List unclaimed issues
12
+ * - claims/issue_list_mine - List my claims
13
+ * - claims/issue_board - View claim board (who's working on what)
14
+ *
15
+ * Work Stealing (4 tools):
16
+ * - claims/issue_mark_stealable - Mark my claim as stealable
17
+ * - claims/issue_steal - Steal a stealable issue
18
+ * - claims/issue_get_stealable - List stealable issues
19
+ * - claims/issue_contest_steal - Contest a steal
20
+ *
21
+ * Load Balancing (3 tools):
22
+ * - claims/agent_load_info - Get agent's current load
23
+ * - claims/swarm_rebalance - Trigger swarm rebalancing
24
+ * - claims/swarm_load_overview - Get swarm-wide load distribution
25
+ *
26
+ * Additionally provides:
27
+ * - claims/claim_history - Get claim history for an issue
28
+ * - claims/claim_metrics - Get claiming metrics
29
+ * - claims/claim_config - Configure claiming behavior
30
+ *
31
+ * Implements ADR-005: MCP-First API Design
32
+ */
33
+ import { z } from 'zod';
34
+ import { randomBytes } from 'crypto';
35
+ // ============================================================================
36
+ // Secure ID Generation
37
+ // ============================================================================
38
+ function generateSecureId(prefix) {
39
+ const timestamp = Date.now().toString(36);
40
+ const random = randomBytes(12).toString('hex');
41
+ return `${prefix}-${timestamp}-${random}`;
42
+ }
43
+ // ============================================================================
44
+ // In-Memory Store (for simple implementation without service)
45
+ // ============================================================================
46
+ const claimStore = new Map();
47
+ const issueStore = new Map();
48
+ // Initialize with some mock data
49
+ function initializeMockData() {
50
+ if (issueStore.size === 0) {
51
+ const mockIssues = [
52
+ {
53
+ id: 'issue-1',
54
+ title: 'Implement user authentication',
55
+ priority: 'high',
56
+ labels: ['feature', 'security'],
57
+ createdAt: new Date().toISOString(),
58
+ },
59
+ {
60
+ id: 'issue-2',
61
+ title: 'Fix memory leak in agent coordinator',
62
+ priority: 'critical',
63
+ labels: ['bug', 'performance'],
64
+ createdAt: new Date().toISOString(),
65
+ },
66
+ {
67
+ id: 'issue-3',
68
+ title: 'Add unit tests for claims module',
69
+ priority: 'medium',
70
+ labels: ['testing'],
71
+ createdAt: new Date().toISOString(),
72
+ },
73
+ ];
74
+ mockIssues.forEach(issue => issueStore.set(issue.id, issue));
75
+ }
76
+ }
77
+ // ============================================================================
78
+ // Input Schemas
79
+ // ============================================================================
80
+ // Core Claiming Schemas
81
+ const issueClaimSchema = z.object({
82
+ issueId: z.string().min(1).describe('Issue ID to claim'),
83
+ claimantType: z.enum(['human', 'agent']).describe('Type of claimant'),
84
+ claimantId: z.string().min(1).describe('ID of the claimant'),
85
+ priority: z.enum(['critical', 'high', 'medium', 'low']).optional()
86
+ .describe('Override priority for the claim'),
87
+ expiresInMs: z.number().int().positive().optional()
88
+ .describe('Claim expiration time in milliseconds'),
89
+ });
90
+ const issueReleaseSchema = z.object({
91
+ issueId: z.string().min(1).describe('Issue ID to release'),
92
+ claimantId: z.string().min(1).describe('ID of the current claimant'),
93
+ reason: z.string().optional().describe('Reason for releasing the claim'),
94
+ });
95
+ const issueHandoffSchema = z.object({
96
+ issueId: z.string().min(1).describe('Issue ID for handoff'),
97
+ fromId: z.string().min(1).describe('Current claimant ID'),
98
+ toId: z.string().optional().describe('Target claimant ID (optional for open handoff)'),
99
+ toType: z.enum(['human', 'agent']).optional().describe('Target claimant type'),
100
+ reason: z.enum(['blocked', 'expertise-needed', 'capacity', 'reassignment', 'other'])
101
+ .describe('Reason for handoff'),
102
+ notes: z.string().optional().describe('Additional notes for handoff'),
103
+ });
104
+ const issueStatusUpdateSchema = z.object({
105
+ issueId: z.string().min(1).describe('Issue ID to update'),
106
+ claimantId: z.string().min(1).describe('Current claimant ID'),
107
+ status: z.enum(['active', 'blocked', 'in-review', 'completed']).describe('New status'),
108
+ progress: z.number().min(0).max(100).optional().describe('Progress percentage (0-100)'),
109
+ notes: z.string().optional().describe('Status update notes'),
110
+ });
111
+ const issueListAvailableSchema = z.object({
112
+ priority: z.enum(['critical', 'high', 'medium', 'low']).optional()
113
+ .describe('Filter by priority'),
114
+ labels: z.array(z.string()).optional().describe('Filter by labels'),
115
+ repository: z.string().optional().describe('Filter by repository'),
116
+ limit: z.number().int().positive().max(100).default(50).describe('Maximum results'),
117
+ offset: z.number().int().nonnegative().default(0).describe('Pagination offset'),
118
+ });
119
+ const issueListMineSchema = z.object({
120
+ claimantId: z.string().min(1).describe('Claimant ID'),
121
+ status: z.enum(['active', 'blocked', 'in-review', 'completed', 'released', 'stolen']).optional()
122
+ .describe('Filter by status'),
123
+ limit: z.number().int().positive().max(100).default(50).describe('Maximum results'),
124
+ offset: z.number().int().nonnegative().default(0).describe('Pagination offset'),
125
+ });
126
+ const issueBoardSchema = z.object({
127
+ includeAgents: z.boolean().default(true).describe('Include agent claims'),
128
+ includeHumans: z.boolean().default(true).describe('Include human claims'),
129
+ groupBy: z.enum(['claimant', 'priority', 'status']).optional()
130
+ .describe('Group claims by field'),
131
+ });
132
+ // Work Stealing Schemas
133
+ const issueMarkStealableSchema = z.object({
134
+ issueId: z.string().min(1).describe('Issue ID to mark as stealable'),
135
+ claimantId: z.string().min(1).describe('Current claimant ID'),
136
+ reason: z.string().optional().describe('Reason for making stealable'),
137
+ });
138
+ const issueStealSchema = z.object({
139
+ issueId: z.string().min(1).describe('Issue ID to steal'),
140
+ stealerId: z.string().min(1).describe('ID of the stealer'),
141
+ stealerType: z.enum(['human', 'agent']).describe('Type of stealer'),
142
+ reason: z.string().optional().describe('Reason for stealing'),
143
+ });
144
+ const issueGetStealableSchema = z.object({
145
+ priority: z.enum(['critical', 'high', 'medium', 'low']).optional()
146
+ .describe('Filter by priority'),
147
+ limit: z.number().int().positive().max(100).default(50).describe('Maximum results'),
148
+ });
149
+ const issueContestStealSchema = z.object({
150
+ issueId: z.string().min(1).describe('Issue ID being contested'),
151
+ contesterId: z.string().min(1).describe('ID of the contester'),
152
+ reason: z.string().min(1).describe('Reason for contesting'),
153
+ });
154
+ // Load Balancing Schemas
155
+ const agentLoadInfoSchema = z.object({
156
+ agentId: z.string().min(1).describe('Agent ID to get load info for'),
157
+ });
158
+ const swarmRebalanceSchema = z.object({
159
+ strategy: z.enum(['round-robin', 'least-loaded', 'priority-based', 'capability-based'])
160
+ .default('least-loaded').describe('Rebalancing strategy'),
161
+ dryRun: z.boolean().default(false).describe('Simulate without making changes'),
162
+ });
163
+ const swarmLoadOverviewSchema = z.object({
164
+ includeRecommendations: z.boolean().default(true)
165
+ .describe('Include optimization recommendations'),
166
+ });
167
+ // Additional Tools Schemas
168
+ const claimHistorySchema = z.object({
169
+ issueId: z.string().min(1).describe('Issue ID to get history for'),
170
+ limit: z.number().int().positive().max(100).default(50).describe('Maximum entries'),
171
+ });
172
+ const claimMetricsSchema = z.object({
173
+ timeRange: z.enum(['1h', '24h', '7d', '30d', 'all']).default('24h')
174
+ .describe('Time range for metrics'),
175
+ });
176
+ const claimConfigSchema = z.object({
177
+ action: z.enum(['get', 'set']).describe('Get or set configuration'),
178
+ config: z.object({
179
+ defaultExpirationMs: z.number().int().positive().optional(),
180
+ maxClaimsPerAgent: z.number().int().positive().optional(),
181
+ contestWindowMs: z.number().int().positive().optional(),
182
+ autoReleaseOnInactivityMs: z.number().int().positive().optional(),
183
+ }).optional().describe('Configuration values (for set action)'),
184
+ });
185
+ // ============================================================================
186
+ // Tool Handlers
187
+ // ============================================================================
188
+ /**
189
+ * Claim an issue to work on
190
+ */
191
+ async function handleIssueClaim(input, context) {
192
+ initializeMockData();
193
+ // Try to use claims service if available
194
+ if (context?.claimsService) {
195
+ const claim = await context.claimsService.claimIssue(input);
196
+ return {
197
+ claimId: claim.id,
198
+ issueId: claim.issueId,
199
+ claimantId: claim.claimantId,
200
+ claimantType: claim.claimantType,
201
+ status: claim.status,
202
+ claimedAt: claim.claimedAt,
203
+ expiresAt: claim.expiresAt,
204
+ };
205
+ }
206
+ // Simple implementation
207
+ const issue = issueStore.get(input.issueId);
208
+ if (!issue) {
209
+ throw new Error(`Issue not found: ${input.issueId}`);
210
+ }
211
+ if (issue.claimedBy) {
212
+ throw new Error(`Issue ${input.issueId} is already claimed by ${issue.claimedBy}`);
213
+ }
214
+ const claimId = generateSecureId('claim');
215
+ const claimedAt = new Date().toISOString();
216
+ const expiresAt = input.expiresInMs
217
+ ? new Date(Date.now() + input.expiresInMs).toISOString()
218
+ : undefined;
219
+ const claim = {
220
+ id: claimId,
221
+ issueId: input.issueId,
222
+ claimantType: input.claimantType,
223
+ claimantId: input.claimantId,
224
+ status: 'active',
225
+ priority: input.priority || issue.priority,
226
+ stealable: false,
227
+ claimedAt,
228
+ lastActivityAt: claimedAt,
229
+ expiresAt,
230
+ };
231
+ claimStore.set(claimId, claim);
232
+ issue.claimedBy = input.claimantId;
233
+ return {
234
+ claimId,
235
+ issueId: input.issueId,
236
+ claimantId: input.claimantId,
237
+ claimantType: input.claimantType,
238
+ status: 'active',
239
+ claimedAt,
240
+ expiresAt,
241
+ };
242
+ }
243
+ /**
244
+ * Release a claim
245
+ */
246
+ async function handleIssueRelease(input, context) {
247
+ if (context?.claimsService) {
248
+ const result = await context.claimsService.releaseClaim(input);
249
+ return {
250
+ released: result.released,
251
+ issueId: input.issueId,
252
+ releasedAt: result.releasedAt,
253
+ reason: input.reason,
254
+ };
255
+ }
256
+ // Simple implementation
257
+ const issue = issueStore.get(input.issueId);
258
+ if (!issue) {
259
+ throw new Error(`Issue not found: ${input.issueId}`);
260
+ }
261
+ if (issue.claimedBy !== input.claimantId) {
262
+ throw new Error(`Issue ${input.issueId} is not claimed by ${input.claimantId}`);
263
+ }
264
+ // Find and update the claim
265
+ for (const claim of claimStore.values()) {
266
+ if (claim.issueId === input.issueId && claim.claimantId === input.claimantId) {
267
+ claim.status = 'released';
268
+ claim.lastActivityAt = new Date().toISOString();
269
+ }
270
+ }
271
+ issue.claimedBy = undefined;
272
+ return {
273
+ released: true,
274
+ issueId: input.issueId,
275
+ releasedAt: new Date().toISOString(),
276
+ reason: input.reason,
277
+ };
278
+ }
279
+ /**
280
+ * Request handoff to another agent/human
281
+ */
282
+ async function handleIssueHandoff(input, context) {
283
+ if (context?.claimsService) {
284
+ const result = await context.claimsService.requestHandoff(input);
285
+ return {
286
+ handoffId: result.handoffId,
287
+ issueId: input.issueId,
288
+ fromId: input.fromId,
289
+ toId: input.toId,
290
+ toType: input.toType,
291
+ status: result.status,
292
+ reason: input.reason,
293
+ createdAt: new Date().toISOString(),
294
+ };
295
+ }
296
+ // Simple implementation
297
+ const handoffId = generateSecureId('handoff');
298
+ return {
299
+ handoffId,
300
+ issueId: input.issueId,
301
+ fromId: input.fromId,
302
+ toId: input.toId,
303
+ toType: input.toType,
304
+ status: 'pending',
305
+ reason: input.reason,
306
+ createdAt: new Date().toISOString(),
307
+ };
308
+ }
309
+ /**
310
+ * Update claim status
311
+ */
312
+ async function handleIssueStatusUpdate(input, context) {
313
+ if (context?.claimsService) {
314
+ const claim = await context.claimsService.updateClaimStatus(input);
315
+ return {
316
+ issueId: claim.issueId,
317
+ status: claim.status,
318
+ progress: input.progress,
319
+ updatedAt: claim.lastActivityAt,
320
+ notes: input.notes,
321
+ };
322
+ }
323
+ // Simple implementation
324
+ for (const claim of claimStore.values()) {
325
+ if (claim.issueId === input.issueId && claim.claimantId === input.claimantId) {
326
+ claim.status = input.status;
327
+ claim.lastActivityAt = new Date().toISOString();
328
+ if (input.progress !== undefined) {
329
+ claim.metadata = { ...claim.metadata, progress: input.progress };
330
+ }
331
+ return {
332
+ issueId: input.issueId,
333
+ status: input.status,
334
+ progress: input.progress,
335
+ updatedAt: claim.lastActivityAt,
336
+ notes: input.notes,
337
+ };
338
+ }
339
+ }
340
+ throw new Error(`No active claim found for issue ${input.issueId} by ${input.claimantId}`);
341
+ }
342
+ /**
343
+ * List unclaimed issues
344
+ */
345
+ async function handleIssueListAvailable(input, context) {
346
+ initializeMockData();
347
+ if (context?.claimsService) {
348
+ const result = await context.claimsService.listAvailableIssues(input);
349
+ return {
350
+ ...result,
351
+ limit: input.limit,
352
+ offset: input.offset,
353
+ };
354
+ }
355
+ // Simple implementation
356
+ let issues = Array.from(issueStore.values()).filter(issue => !issue.claimedBy);
357
+ if (input.priority) {
358
+ issues = issues.filter(issue => issue.priority === input.priority);
359
+ }
360
+ if (input.labels && input.labels.length > 0) {
361
+ issues = issues.filter(issue => input.labels.some(label => issue.labels?.includes(label)));
362
+ }
363
+ if (input.repository) {
364
+ issues = issues.filter(issue => issue.repository === input.repository);
365
+ }
366
+ const total = issues.length;
367
+ const paginated = issues.slice(input.offset, input.offset + input.limit);
368
+ return {
369
+ issues: paginated,
370
+ total,
371
+ limit: input.limit,
372
+ offset: input.offset,
373
+ };
374
+ }
375
+ /**
376
+ * List my claims
377
+ */
378
+ async function handleIssueListMine(input, context) {
379
+ if (context?.claimsService) {
380
+ const result = await context.claimsService.listMyClaims(input);
381
+ return {
382
+ ...result,
383
+ limit: input.limit,
384
+ offset: input.offset,
385
+ };
386
+ }
387
+ // Simple implementation
388
+ let claims = Array.from(claimStore.values())
389
+ .filter(claim => claim.claimantId === input.claimantId);
390
+ if (input.status) {
391
+ claims = claims.filter(claim => claim.status === input.status);
392
+ }
393
+ const total = claims.length;
394
+ const paginated = claims.slice(input.offset, input.offset + input.limit);
395
+ return {
396
+ claims: paginated,
397
+ total,
398
+ limit: input.limit,
399
+ offset: input.offset,
400
+ };
401
+ }
402
+ /**
403
+ * View claim board
404
+ */
405
+ async function handleIssueBoard(input, context) {
406
+ if (context?.claimsService) {
407
+ const result = await context.claimsService.getClaimBoard(input);
408
+ return {
409
+ claims: result.claims,
410
+ totalClaims: result.claims.length,
411
+ byClaimant: result.byClaimant
412
+ ? Object.fromEntries(Object.entries(result.byClaimant).map(([k, v]) => [k, v.length]))
413
+ : undefined,
414
+ byPriority: result.byPriority
415
+ ? Object.fromEntries(Object.entries(result.byPriority).map(([k, v]) => [k, v.length]))
416
+ : undefined,
417
+ byStatus: result.byStatus
418
+ ? Object.fromEntries(Object.entries(result.byStatus).map(([k, v]) => [k, v.length]))
419
+ : undefined,
420
+ };
421
+ }
422
+ // Simple implementation
423
+ let claims = Array.from(claimStore.values());
424
+ if (!input.includeAgents) {
425
+ claims = claims.filter(c => c.claimantType !== 'agent');
426
+ }
427
+ if (!input.includeHumans) {
428
+ claims = claims.filter(c => c.claimantType !== 'human');
429
+ }
430
+ const result = {
431
+ claims,
432
+ totalClaims: claims.length,
433
+ };
434
+ if (input.groupBy === 'claimant') {
435
+ result.byClaimant = {};
436
+ claims.forEach(c => {
437
+ result.byClaimant[c.claimantId] = (result.byClaimant[c.claimantId] || 0) + 1;
438
+ });
439
+ }
440
+ else if (input.groupBy === 'priority') {
441
+ result.byPriority = { critical: 0, high: 0, medium: 0, low: 0 };
442
+ claims.forEach(c => {
443
+ result.byPriority[c.priority]++;
444
+ });
445
+ }
446
+ else if (input.groupBy === 'status') {
447
+ result.byStatus = { active: 0, blocked: 0, 'in-review': 0, completed: 0, released: 0, stolen: 0 };
448
+ claims.forEach(c => {
449
+ result.byStatus[c.status]++;
450
+ });
451
+ }
452
+ return result;
453
+ }
454
+ /**
455
+ * Mark claim as stealable
456
+ */
457
+ async function handleIssueMarkStealable(input, context) {
458
+ if (context?.claimsService) {
459
+ const result = await context.claimsService.markStealable(input);
460
+ return {
461
+ marked: result.marked,
462
+ issueId: input.issueId,
463
+ markedAt: result.markedAt,
464
+ reason: input.reason,
465
+ };
466
+ }
467
+ // Simple implementation
468
+ for (const claim of claimStore.values()) {
469
+ if (claim.issueId === input.issueId && claim.claimantId === input.claimantId) {
470
+ claim.stealable = true;
471
+ claim.stealableReason = input.reason;
472
+ claim.lastActivityAt = new Date().toISOString();
473
+ return {
474
+ marked: true,
475
+ issueId: input.issueId,
476
+ markedAt: claim.lastActivityAt,
477
+ reason: input.reason,
478
+ };
479
+ }
480
+ }
481
+ throw new Error(`No active claim found for issue ${input.issueId} by ${input.claimantId}`);
482
+ }
483
+ /**
484
+ * Steal a stealable issue
485
+ */
486
+ async function handleIssueSteal(input, context) {
487
+ if (context?.claimsService) {
488
+ const result = await context.claimsService.stealClaim(input);
489
+ return {
490
+ stolen: result.stolen,
491
+ issueId: input.issueId,
492
+ newClaimId: result.claim.id,
493
+ previousClaimant: result.previousClaimant,
494
+ contestWindowMs: result.contestWindow,
495
+ stolenAt: result.claim.claimedAt,
496
+ };
497
+ }
498
+ // Simple implementation
499
+ for (const claim of claimStore.values()) {
500
+ if (claim.issueId === input.issueId && claim.stealable) {
501
+ const previousClaimant = claim.claimantId;
502
+ // Update old claim
503
+ claim.status = 'stolen';
504
+ claim.lastActivityAt = new Date().toISOString();
505
+ // Create new claim
506
+ const newClaimId = generateSecureId('claim');
507
+ const stolenAt = new Date().toISOString();
508
+ const newClaim = {
509
+ id: newClaimId,
510
+ issueId: input.issueId,
511
+ claimantType: input.stealerType,
512
+ claimantId: input.stealerId,
513
+ status: 'active',
514
+ priority: claim.priority,
515
+ stealable: false,
516
+ claimedAt: stolenAt,
517
+ lastActivityAt: stolenAt,
518
+ };
519
+ claimStore.set(newClaimId, newClaim);
520
+ // Update issue
521
+ const issue = issueStore.get(input.issueId);
522
+ if (issue) {
523
+ issue.claimedBy = input.stealerId;
524
+ }
525
+ return {
526
+ stolen: true,
527
+ issueId: input.issueId,
528
+ newClaimId,
529
+ previousClaimant,
530
+ contestWindowMs: 300000, // 5 minutes
531
+ stolenAt,
532
+ };
533
+ }
534
+ }
535
+ throw new Error(`Issue ${input.issueId} is not stealable or not claimed`);
536
+ }
537
+ /**
538
+ * Get stealable issues
539
+ */
540
+ async function handleIssueGetStealable(input, context) {
541
+ if (context?.claimsService) {
542
+ const result = await context.claimsService.getStealableIssues(input);
543
+ return {
544
+ issues: result.issues.map(issue => ({
545
+ issueId: issue.id,
546
+ title: issue.title,
547
+ priority: issue.priority,
548
+ currentClaimant: issue.claimedBy || 'unknown',
549
+ stealableReason: issue.stealableReason,
550
+ })),
551
+ total: result.total,
552
+ };
553
+ }
554
+ // Simple implementation
555
+ let stealableClaims = Array.from(claimStore.values()).filter(c => c.stealable);
556
+ if (input.priority) {
557
+ stealableClaims = stealableClaims.filter(c => c.priority === input.priority);
558
+ }
559
+ const issues = stealableClaims.slice(0, input.limit).map(claim => {
560
+ const issue = issueStore.get(claim.issueId);
561
+ return {
562
+ issueId: claim.issueId,
563
+ title: issue?.title || 'Unknown',
564
+ priority: claim.priority,
565
+ currentClaimant: claim.claimantId,
566
+ stealableReason: claim.stealableReason,
567
+ };
568
+ });
569
+ return {
570
+ issues,
571
+ total: stealableClaims.length,
572
+ };
573
+ }
574
+ /**
575
+ * Contest a steal
576
+ */
577
+ async function handleIssueContestSteal(input, context) {
578
+ if (context?.claimsService) {
579
+ const result = await context.claimsService.contestSteal(input);
580
+ return {
581
+ contested: result.contested,
582
+ contestId: generateSecureId('contest'),
583
+ issueId: input.issueId,
584
+ contesterId: input.contesterId,
585
+ status: result.resolution,
586
+ contestedAt: result.resolvedAt || new Date().toISOString(),
587
+ };
588
+ }
589
+ // Simple implementation
590
+ return {
591
+ contested: true,
592
+ contestId: generateSecureId('contest'),
593
+ issueId: input.issueId,
594
+ contesterId: input.contesterId,
595
+ status: 'pending',
596
+ contestedAt: new Date().toISOString(),
597
+ };
598
+ }
599
+ /**
600
+ * Get agent load info
601
+ */
602
+ async function handleAgentLoadInfo(input, context) {
603
+ if (context?.claimsService) {
604
+ return context.claimsService.getAgentLoad(input);
605
+ }
606
+ // Simple implementation
607
+ const claims = Array.from(claimStore.values())
608
+ .filter(c => c.claimantId === input.agentId && c.status === 'active');
609
+ return {
610
+ agentId: input.agentId,
611
+ agentType: 'worker',
612
+ currentClaims: claims.length,
613
+ maxClaims: 5,
614
+ utilizationPercent: Math.min(100, (claims.length / 5) * 100),
615
+ activeTasks: claims.length,
616
+ queuedTasks: 0,
617
+ averageTaskDuration: 3600000, // 1 hour
618
+ lastActivityAt: new Date().toISOString(),
619
+ };
620
+ }
621
+ /**
622
+ * Trigger swarm rebalancing
623
+ */
624
+ async function handleSwarmRebalance(input, context) {
625
+ if (context?.claimsService) {
626
+ const result = await context.claimsService.rebalanceSwarm(input);
627
+ return {
628
+ rebalanced: result.rebalanced,
629
+ strategy: input.strategy,
630
+ changes: result.changes,
631
+ dryRun: result.dryRun,
632
+ rebalancedAt: new Date().toISOString(),
633
+ };
634
+ }
635
+ // Simple implementation - no actual rebalancing
636
+ return {
637
+ rebalanced: !input.dryRun,
638
+ strategy: input.strategy,
639
+ changes: [],
640
+ dryRun: input.dryRun,
641
+ rebalancedAt: new Date().toISOString(),
642
+ };
643
+ }
644
+ /**
645
+ * Get swarm-wide load overview
646
+ */
647
+ async function handleSwarmLoadOverview(input, context) {
648
+ if (context?.claimsService) {
649
+ const result = await context.claimsService.getLoadOverview();
650
+ return {
651
+ ...result,
652
+ agents: result.agents.map(a => ({
653
+ agentId: a.agentId,
654
+ currentClaims: a.currentClaims,
655
+ utilizationPercent: a.utilizationPercent,
656
+ })),
657
+ };
658
+ }
659
+ // Simple implementation
660
+ const claimsByAgent = new Map();
661
+ for (const claim of claimStore.values()) {
662
+ if (claim.status === 'active') {
663
+ claimsByAgent.set(claim.claimantId, (claimsByAgent.get(claim.claimantId) || 0) + 1);
664
+ }
665
+ }
666
+ const agents = Array.from(claimsByAgent.entries()).map(([agentId, claims]) => ({
667
+ agentId,
668
+ currentClaims: claims,
669
+ utilizationPercent: Math.min(100, (claims / 5) * 100),
670
+ }));
671
+ const totalClaims = Array.from(claimsByAgent.values()).reduce((a, b) => a + b, 0);
672
+ const avgLoad = agents.length > 0
673
+ ? agents.reduce((a, b) => a + b.utilizationPercent, 0) / agents.length
674
+ : 0;
675
+ const result = {
676
+ totalAgents: agents.length,
677
+ totalClaims,
678
+ averageLoad: Math.round(avgLoad * 100) / 100,
679
+ agents,
680
+ bottlenecks: [],
681
+ recommendations: [],
682
+ };
683
+ if (input.includeRecommendations) {
684
+ const overloaded = agents.filter(a => a.utilizationPercent > 80);
685
+ if (overloaded.length > 0) {
686
+ result.bottlenecks = overloaded.map(a => a.agentId);
687
+ result.recommendations.push('Consider rebalancing claims to reduce load on overloaded agents');
688
+ }
689
+ if (avgLoad > 70) {
690
+ result.recommendations.push('Swarm is under high load. Consider scaling up agent count.');
691
+ }
692
+ if (avgLoad < 30 && agents.length > 1) {
693
+ result.recommendations.push('Swarm has low utilization. Consider consolidating agents.');
694
+ }
695
+ }
696
+ return result;
697
+ }
698
+ /**
699
+ * Get claim history
700
+ */
701
+ async function handleClaimHistory(input, context) {
702
+ if (context?.claimsService) {
703
+ const result = await context.claimsService.getClaimHistory(input);
704
+ return {
705
+ issueId: input.issueId,
706
+ history: result.history,
707
+ total: result.total,
708
+ };
709
+ }
710
+ // Simple implementation - mock history
711
+ const claims = Array.from(claimStore.values())
712
+ .filter(c => c.issueId === input.issueId)
713
+ .sort((a, b) => new Date(b.claimedAt).getTime() - new Date(a.claimedAt).getTime());
714
+ const history = claims.flatMap(claim => [
715
+ {
716
+ timestamp: claim.claimedAt,
717
+ action: 'claimed',
718
+ actorId: claim.claimantId,
719
+ actorType: claim.claimantType,
720
+ },
721
+ ...(claim.status !== 'active' ? [{
722
+ timestamp: claim.lastActivityAt,
723
+ action: claim.status,
724
+ actorId: claim.claimantId,
725
+ actorType: claim.claimantType,
726
+ }] : []),
727
+ ]).slice(0, input.limit);
728
+ return {
729
+ issueId: input.issueId,
730
+ history,
731
+ total: history.length,
732
+ };
733
+ }
734
+ /**
735
+ * Get claim metrics
736
+ */
737
+ async function handleClaimMetrics(input, context) {
738
+ if (context?.claimsService) {
739
+ const metrics = await context.claimsService.getMetrics();
740
+ return {
741
+ timeRange: input.timeRange,
742
+ totalClaims: metrics.totalClaims,
743
+ activeClaims: metrics.activeClaims,
744
+ completedClaims: metrics.completedClaims,
745
+ stolenClaims: metrics.stolenClaims,
746
+ averageClaimDurationMs: metrics.averageClaimDuration,
747
+ claimsByPriority: metrics.claimsByPriority,
748
+ claimsByStatus: metrics.claimsByStatus,
749
+ };
750
+ }
751
+ // Simple implementation
752
+ const claims = Array.from(claimStore.values());
753
+ const byPriority = { critical: 0, high: 0, medium: 0, low: 0 };
754
+ const byStatus = {
755
+ active: 0, blocked: 0, 'in-review': 0, completed: 0, released: 0, stolen: 0,
756
+ };
757
+ claims.forEach(c => {
758
+ byPriority[c.priority]++;
759
+ byStatus[c.status]++;
760
+ });
761
+ return {
762
+ timeRange: input.timeRange,
763
+ totalClaims: claims.length,
764
+ activeClaims: byStatus.active,
765
+ completedClaims: byStatus.completed,
766
+ stolenClaims: byStatus.stolen,
767
+ averageClaimDurationMs: 3600000, // 1 hour mock
768
+ claimsByPriority: byPriority,
769
+ claimsByStatus: byStatus,
770
+ };
771
+ }
772
+ /**
773
+ * Get/set claim configuration
774
+ */
775
+ async function handleClaimConfig(input, _context) {
776
+ // Default configuration
777
+ const defaultConfig = {
778
+ defaultExpirationMs: 86400000, // 24 hours
779
+ maxClaimsPerAgent: 5,
780
+ contestWindowMs: 300000, // 5 minutes
781
+ autoReleaseOnInactivityMs: 7200000, // 2 hours
782
+ };
783
+ if (input.action === 'get') {
784
+ return {
785
+ action: 'get',
786
+ config: defaultConfig,
787
+ };
788
+ }
789
+ // Set action
790
+ const newConfig = {
791
+ ...defaultConfig,
792
+ ...input.config,
793
+ };
794
+ return {
795
+ action: 'set',
796
+ config: newConfig,
797
+ updatedAt: new Date().toISOString(),
798
+ };
799
+ }
800
+ // ============================================================================
801
+ // Tool Definitions
802
+ // ============================================================================
803
+ // Core Claiming Tools
804
+ export const issueClaimTool = {
805
+ name: 'claims/issue_claim',
806
+ description: 'Claim an issue to work on. Prevents duplicate work by ensuring only one agent/human works on an issue at a time.',
807
+ inputSchema: {
808
+ type: 'object',
809
+ properties: {
810
+ issueId: { type: 'string', description: 'Issue ID to claim' },
811
+ claimantType: { type: 'string', enum: ['human', 'agent'], description: 'Type of claimant' },
812
+ claimantId: { type: 'string', description: 'ID of the claimant' },
813
+ priority: {
814
+ type: 'string',
815
+ enum: ['critical', 'high', 'medium', 'low'],
816
+ description: 'Override priority for the claim',
817
+ },
818
+ expiresInMs: {
819
+ type: 'number',
820
+ description: 'Claim expiration time in milliseconds',
821
+ minimum: 1,
822
+ },
823
+ },
824
+ required: ['issueId', 'claimantType', 'claimantId'],
825
+ },
826
+ handler: async (input, context) => {
827
+ const validated = issueClaimSchema.parse(input);
828
+ return handleIssueClaim(validated, context);
829
+ },
830
+ category: 'claims',
831
+ tags: ['claims', 'issue', 'coordination'],
832
+ version: '1.0.0',
833
+ };
834
+ export const issueReleaseTool = {
835
+ name: 'claims/issue_release',
836
+ description: 'Release a claim on an issue, making it available for others to work on.',
837
+ inputSchema: {
838
+ type: 'object',
839
+ properties: {
840
+ issueId: { type: 'string', description: 'Issue ID to release' },
841
+ claimantId: { type: 'string', description: 'ID of the current claimant' },
842
+ reason: { type: 'string', description: 'Reason for releasing the claim' },
843
+ },
844
+ required: ['issueId', 'claimantId'],
845
+ },
846
+ handler: async (input, context) => {
847
+ const validated = issueReleaseSchema.parse(input);
848
+ return handleIssueRelease(validated, context);
849
+ },
850
+ category: 'claims',
851
+ tags: ['claims', 'issue', 'release'],
852
+ version: '1.0.0',
853
+ };
854
+ export const issueHandoffTool = {
855
+ name: 'claims/issue_handoff',
856
+ description: 'Request handoff of an issue to another agent or human. Useful when blocked or needing specific expertise.',
857
+ inputSchema: {
858
+ type: 'object',
859
+ properties: {
860
+ issueId: { type: 'string', description: 'Issue ID for handoff' },
861
+ fromId: { type: 'string', description: 'Current claimant ID' },
862
+ toId: { type: 'string', description: 'Target claimant ID (optional for open handoff)' },
863
+ toType: { type: 'string', enum: ['human', 'agent'], description: 'Target claimant type' },
864
+ reason: {
865
+ type: 'string',
866
+ enum: ['blocked', 'expertise-needed', 'capacity', 'reassignment', 'other'],
867
+ description: 'Reason for handoff',
868
+ },
869
+ notes: { type: 'string', description: 'Additional notes for handoff' },
870
+ },
871
+ required: ['issueId', 'fromId', 'reason'],
872
+ },
873
+ handler: async (input, context) => {
874
+ const validated = issueHandoffSchema.parse(input);
875
+ return handleIssueHandoff(validated, context);
876
+ },
877
+ category: 'claims',
878
+ tags: ['claims', 'issue', 'handoff', 'coordination'],
879
+ version: '1.0.0',
880
+ };
881
+ export const issueStatusUpdateTool = {
882
+ name: 'claims/issue_status_update',
883
+ description: 'Update the status of a claimed issue. Track progress and communicate blockers.',
884
+ inputSchema: {
885
+ type: 'object',
886
+ properties: {
887
+ issueId: { type: 'string', description: 'Issue ID to update' },
888
+ claimantId: { type: 'string', description: 'Current claimant ID' },
889
+ status: {
890
+ type: 'string',
891
+ enum: ['active', 'blocked', 'in-review', 'completed'],
892
+ description: 'New status',
893
+ },
894
+ progress: {
895
+ type: 'number',
896
+ description: 'Progress percentage (0-100)',
897
+ minimum: 0,
898
+ maximum: 100,
899
+ },
900
+ notes: { type: 'string', description: 'Status update notes' },
901
+ },
902
+ required: ['issueId', 'claimantId', 'status'],
903
+ },
904
+ handler: async (input, context) => {
905
+ const validated = issueStatusUpdateSchema.parse(input);
906
+ return handleIssueStatusUpdate(validated, context);
907
+ },
908
+ category: 'claims',
909
+ tags: ['claims', 'issue', 'status', 'progress'],
910
+ version: '1.0.0',
911
+ };
912
+ export const issueListAvailableTool = {
913
+ name: 'claims/issue_list_available',
914
+ description: 'List all unclaimed issues available for work. Filter by priority, labels, or repository.',
915
+ inputSchema: {
916
+ type: 'object',
917
+ properties: {
918
+ priority: {
919
+ type: 'string',
920
+ enum: ['critical', 'high', 'medium', 'low'],
921
+ description: 'Filter by priority',
922
+ },
923
+ labels: {
924
+ type: 'array',
925
+ items: { type: 'string' },
926
+ description: 'Filter by labels',
927
+ },
928
+ repository: { type: 'string', description: 'Filter by repository' },
929
+ limit: {
930
+ type: 'number',
931
+ description: 'Maximum results',
932
+ minimum: 1,
933
+ maximum: 100,
934
+ default: 50,
935
+ },
936
+ offset: {
937
+ type: 'number',
938
+ description: 'Pagination offset',
939
+ minimum: 0,
940
+ default: 0,
941
+ },
942
+ },
943
+ },
944
+ handler: async (input, context) => {
945
+ const validated = issueListAvailableSchema.parse(input);
946
+ return handleIssueListAvailable(validated, context);
947
+ },
948
+ category: 'claims',
949
+ tags: ['claims', 'issue', 'list', 'available'],
950
+ version: '1.0.0',
951
+ cacheable: true,
952
+ cacheTTL: 5000,
953
+ };
954
+ export const issueListMineTool = {
955
+ name: 'claims/issue_list_mine',
956
+ description: 'List all issues claimed by a specific claimant. Filter by status.',
957
+ inputSchema: {
958
+ type: 'object',
959
+ properties: {
960
+ claimantId: { type: 'string', description: 'Claimant ID' },
961
+ status: {
962
+ type: 'string',
963
+ enum: ['active', 'blocked', 'in-review', 'completed', 'released', 'stolen'],
964
+ description: 'Filter by status',
965
+ },
966
+ limit: {
967
+ type: 'number',
968
+ description: 'Maximum results',
969
+ minimum: 1,
970
+ maximum: 100,
971
+ default: 50,
972
+ },
973
+ offset: {
974
+ type: 'number',
975
+ description: 'Pagination offset',
976
+ minimum: 0,
977
+ default: 0,
978
+ },
979
+ },
980
+ required: ['claimantId'],
981
+ },
982
+ handler: async (input, context) => {
983
+ const validated = issueListMineSchema.parse(input);
984
+ return handleIssueListMine(validated, context);
985
+ },
986
+ category: 'claims',
987
+ tags: ['claims', 'issue', 'list', 'my-claims'],
988
+ version: '1.0.0',
989
+ cacheable: true,
990
+ cacheTTL: 2000,
991
+ };
992
+ export const issueBoardTool = {
993
+ name: 'claims/issue_board',
994
+ description: 'View the claim board showing who is working on what. Group by claimant, priority, or status.',
995
+ inputSchema: {
996
+ type: 'object',
997
+ properties: {
998
+ includeAgents: {
999
+ type: 'boolean',
1000
+ description: 'Include agent claims',
1001
+ default: true,
1002
+ },
1003
+ includeHumans: {
1004
+ type: 'boolean',
1005
+ description: 'Include human claims',
1006
+ default: true,
1007
+ },
1008
+ groupBy: {
1009
+ type: 'string',
1010
+ enum: ['claimant', 'priority', 'status'],
1011
+ description: 'Group claims by field',
1012
+ },
1013
+ },
1014
+ },
1015
+ handler: async (input, context) => {
1016
+ const validated = issueBoardSchema.parse(input);
1017
+ return handleIssueBoard(validated, context);
1018
+ },
1019
+ category: 'claims',
1020
+ tags: ['claims', 'board', 'overview', 'coordination'],
1021
+ version: '1.0.0',
1022
+ cacheable: true,
1023
+ cacheTTL: 5000,
1024
+ };
1025
+ // Work Stealing Tools
1026
+ export const issueMarkStealableTool = {
1027
+ name: 'claims/issue_mark_stealable',
1028
+ description: 'Mark a claimed issue as stealable, allowing other agents/humans to take over the work.',
1029
+ inputSchema: {
1030
+ type: 'object',
1031
+ properties: {
1032
+ issueId: { type: 'string', description: 'Issue ID to mark as stealable' },
1033
+ claimantId: { type: 'string', description: 'Current claimant ID' },
1034
+ reason: { type: 'string', description: 'Reason for making stealable' },
1035
+ },
1036
+ required: ['issueId', 'claimantId'],
1037
+ },
1038
+ handler: async (input, context) => {
1039
+ const validated = issueMarkStealableSchema.parse(input);
1040
+ return handleIssueMarkStealable(validated, context);
1041
+ },
1042
+ category: 'claims',
1043
+ tags: ['claims', 'stealing', 'mark'],
1044
+ version: '1.0.0',
1045
+ };
1046
+ export const issueStealTool = {
1047
+ name: 'claims/issue_steal',
1048
+ description: 'Steal a stealable issue from another claimant. The previous claimant has a contest window to object.',
1049
+ inputSchema: {
1050
+ type: 'object',
1051
+ properties: {
1052
+ issueId: { type: 'string', description: 'Issue ID to steal' },
1053
+ stealerId: { type: 'string', description: 'ID of the stealer' },
1054
+ stealerType: { type: 'string', enum: ['human', 'agent'], description: 'Type of stealer' },
1055
+ reason: { type: 'string', description: 'Reason for stealing' },
1056
+ },
1057
+ required: ['issueId', 'stealerId', 'stealerType'],
1058
+ },
1059
+ handler: async (input, context) => {
1060
+ const validated = issueStealSchema.parse(input);
1061
+ return handleIssueSteal(validated, context);
1062
+ },
1063
+ category: 'claims',
1064
+ tags: ['claims', 'stealing', 'takeover'],
1065
+ version: '1.0.0',
1066
+ };
1067
+ export const issueGetStealableTool = {
1068
+ name: 'claims/issue_get_stealable',
1069
+ description: 'List all issues marked as stealable. Filter by priority.',
1070
+ inputSchema: {
1071
+ type: 'object',
1072
+ properties: {
1073
+ priority: {
1074
+ type: 'string',
1075
+ enum: ['critical', 'high', 'medium', 'low'],
1076
+ description: 'Filter by priority',
1077
+ },
1078
+ limit: {
1079
+ type: 'number',
1080
+ description: 'Maximum results',
1081
+ minimum: 1,
1082
+ maximum: 100,
1083
+ default: 50,
1084
+ },
1085
+ },
1086
+ },
1087
+ handler: async (input, context) => {
1088
+ const validated = issueGetStealableSchema.parse(input);
1089
+ return handleIssueGetStealable(validated, context);
1090
+ },
1091
+ category: 'claims',
1092
+ tags: ['claims', 'stealing', 'list'],
1093
+ version: '1.0.0',
1094
+ cacheable: true,
1095
+ cacheTTL: 5000,
1096
+ };
1097
+ export const issueContestStealTool = {
1098
+ name: 'claims/issue_contest_steal',
1099
+ description: 'Contest a steal within the contest window. Provide a reason for the contest.',
1100
+ inputSchema: {
1101
+ type: 'object',
1102
+ properties: {
1103
+ issueId: { type: 'string', description: 'Issue ID being contested' },
1104
+ contesterId: { type: 'string', description: 'ID of the contester' },
1105
+ reason: { type: 'string', description: 'Reason for contesting' },
1106
+ },
1107
+ required: ['issueId', 'contesterId', 'reason'],
1108
+ },
1109
+ handler: async (input, context) => {
1110
+ const validated = issueContestStealSchema.parse(input);
1111
+ return handleIssueContestSteal(validated, context);
1112
+ },
1113
+ category: 'claims',
1114
+ tags: ['claims', 'stealing', 'contest'],
1115
+ version: '1.0.0',
1116
+ };
1117
+ // Load Balancing Tools
1118
+ export const agentLoadInfoTool = {
1119
+ name: 'claims/agent_load_info',
1120
+ description: 'Get current load information for a specific agent including claims, tasks, and utilization.',
1121
+ inputSchema: {
1122
+ type: 'object',
1123
+ properties: {
1124
+ agentId: { type: 'string', description: 'Agent ID to get load info for' },
1125
+ },
1126
+ required: ['agentId'],
1127
+ },
1128
+ handler: async (input, context) => {
1129
+ const validated = agentLoadInfoSchema.parse(input);
1130
+ return handleAgentLoadInfo(validated, context);
1131
+ },
1132
+ category: 'claims',
1133
+ tags: ['claims', 'load', 'agent', 'metrics'],
1134
+ version: '1.0.0',
1135
+ cacheable: true,
1136
+ cacheTTL: 2000,
1137
+ };
1138
+ export const swarmRebalanceTool = {
1139
+ name: 'claims/swarm_rebalance',
1140
+ description: 'Trigger rebalancing of claims across the swarm to optimize load distribution.',
1141
+ inputSchema: {
1142
+ type: 'object',
1143
+ properties: {
1144
+ strategy: {
1145
+ type: 'string',
1146
+ enum: ['round-robin', 'least-loaded', 'priority-based', 'capability-based'],
1147
+ description: 'Rebalancing strategy',
1148
+ default: 'least-loaded',
1149
+ },
1150
+ dryRun: {
1151
+ type: 'boolean',
1152
+ description: 'Simulate without making changes',
1153
+ default: false,
1154
+ },
1155
+ },
1156
+ },
1157
+ handler: async (input, context) => {
1158
+ const validated = swarmRebalanceSchema.parse(input);
1159
+ return handleSwarmRebalance(validated, context);
1160
+ },
1161
+ category: 'claims',
1162
+ tags: ['claims', 'load', 'swarm', 'rebalance'],
1163
+ version: '1.0.0',
1164
+ };
1165
+ export const swarmLoadOverviewTool = {
1166
+ name: 'claims/swarm_load_overview',
1167
+ description: 'Get swarm-wide load distribution including all agents, bottlenecks, and optimization recommendations.',
1168
+ inputSchema: {
1169
+ type: 'object',
1170
+ properties: {
1171
+ includeRecommendations: {
1172
+ type: 'boolean',
1173
+ description: 'Include optimization recommendations',
1174
+ default: true,
1175
+ },
1176
+ },
1177
+ },
1178
+ handler: async (input, context) => {
1179
+ const validated = swarmLoadOverviewSchema.parse(input);
1180
+ return handleSwarmLoadOverview(validated, context);
1181
+ },
1182
+ category: 'claims',
1183
+ tags: ['claims', 'load', 'swarm', 'overview', 'metrics'],
1184
+ version: '1.0.0',
1185
+ cacheable: true,
1186
+ cacheTTL: 5000,
1187
+ };
1188
+ // Additional Tools
1189
+ export const claimHistoryTool = {
1190
+ name: 'claims/claim_history',
1191
+ description: 'Get the claim history for a specific issue showing all past claims and actions.',
1192
+ inputSchema: {
1193
+ type: 'object',
1194
+ properties: {
1195
+ issueId: { type: 'string', description: 'Issue ID to get history for' },
1196
+ limit: {
1197
+ type: 'number',
1198
+ description: 'Maximum entries',
1199
+ minimum: 1,
1200
+ maximum: 100,
1201
+ default: 50,
1202
+ },
1203
+ },
1204
+ required: ['issueId'],
1205
+ },
1206
+ handler: async (input, context) => {
1207
+ const validated = claimHistorySchema.parse(input);
1208
+ return handleClaimHistory(validated, context);
1209
+ },
1210
+ category: 'claims',
1211
+ tags: ['claims', 'history', 'audit'],
1212
+ version: '1.0.0',
1213
+ cacheable: true,
1214
+ cacheTTL: 10000,
1215
+ };
1216
+ export const claimMetricsTool = {
1217
+ name: 'claims/claim_metrics',
1218
+ description: 'Get claiming metrics including totals, averages, and distributions by priority and status.',
1219
+ inputSchema: {
1220
+ type: 'object',
1221
+ properties: {
1222
+ timeRange: {
1223
+ type: 'string',
1224
+ enum: ['1h', '24h', '7d', '30d', 'all'],
1225
+ description: 'Time range for metrics',
1226
+ default: '24h',
1227
+ },
1228
+ },
1229
+ },
1230
+ handler: async (input, context) => {
1231
+ const validated = claimMetricsSchema.parse(input);
1232
+ return handleClaimMetrics(validated, context);
1233
+ },
1234
+ category: 'claims',
1235
+ tags: ['claims', 'metrics', 'analytics'],
1236
+ version: '1.0.0',
1237
+ cacheable: true,
1238
+ cacheTTL: 30000,
1239
+ };
1240
+ export const claimConfigTool = {
1241
+ name: 'claims/claim_config',
1242
+ description: 'Get or set claiming configuration including expiration times, limits, and contest windows.',
1243
+ inputSchema: {
1244
+ type: 'object',
1245
+ properties: {
1246
+ action: { type: 'string', enum: ['get', 'set'], description: 'Get or set configuration' },
1247
+ config: {
1248
+ type: 'object',
1249
+ description: 'Configuration values (for set action)',
1250
+ properties: {
1251
+ defaultExpirationMs: { type: 'number', minimum: 1 },
1252
+ maxClaimsPerAgent: { type: 'number', minimum: 1 },
1253
+ contestWindowMs: { type: 'number', minimum: 1 },
1254
+ autoReleaseOnInactivityMs: { type: 'number', minimum: 1 },
1255
+ },
1256
+ },
1257
+ },
1258
+ required: ['action'],
1259
+ },
1260
+ handler: async (input, context) => {
1261
+ const validated = claimConfigSchema.parse(input);
1262
+ return handleClaimConfig(validated, context);
1263
+ },
1264
+ category: 'claims',
1265
+ tags: ['claims', 'config', 'settings'],
1266
+ version: '1.0.0',
1267
+ };
1268
+ // ============================================================================
1269
+ // Tool Collections
1270
+ // ============================================================================
1271
+ /**
1272
+ * Core claiming tools (7 tools)
1273
+ */
1274
+ export const coreClaimingTools = [
1275
+ issueClaimTool,
1276
+ issueReleaseTool,
1277
+ issueHandoffTool,
1278
+ issueStatusUpdateTool,
1279
+ issueListAvailableTool,
1280
+ issueListMineTool,
1281
+ issueBoardTool,
1282
+ ];
1283
+ /**
1284
+ * Work stealing tools (4 tools)
1285
+ */
1286
+ export const workStealingTools = [
1287
+ issueMarkStealableTool,
1288
+ issueStealTool,
1289
+ issueGetStealableTool,
1290
+ issueContestStealTool,
1291
+ ];
1292
+ /**
1293
+ * Load balancing tools (3 tools)
1294
+ */
1295
+ export const loadBalancingTools = [
1296
+ agentLoadInfoTool,
1297
+ swarmRebalanceTool,
1298
+ swarmLoadOverviewTool,
1299
+ ];
1300
+ /**
1301
+ * Additional tools (3 tools)
1302
+ */
1303
+ export const additionalClaimsTools = [
1304
+ claimHistoryTool,
1305
+ claimMetricsTool,
1306
+ claimConfigTool,
1307
+ ];
1308
+ /**
1309
+ * All claims tools (17 tools total)
1310
+ */
1311
+ export const claimsTools = [
1312
+ ...coreClaimingTools,
1313
+ ...workStealingTools,
1314
+ ...loadBalancingTools,
1315
+ ...additionalClaimsTools,
1316
+ ];
1317
+ // ============================================================================
1318
+ // Registration Function
1319
+ // ============================================================================
1320
+ /**
1321
+ * Register all claims tools with an MCP server or tool registry
1322
+ *
1323
+ * @param registry - Tool registry or server to register with
1324
+ * @returns Number of tools registered
1325
+ *
1326
+ * @example
1327
+ * ```typescript
1328
+ * import { registerClaimsTools, claimsTools } from '@sparkleideas/claims';
1329
+ *
1330
+ * // Register all tools
1331
+ * const count = registerClaimsTools(server);
1332
+ * console.log(`Registered ${count} claims tools`);
1333
+ *
1334
+ * // Or use tools directly
1335
+ * server.registerTools(claimsTools);
1336
+ * ```
1337
+ */
1338
+ export function registerClaimsTools(registry) {
1339
+ const registerFn = registry.registerTool || registry.register;
1340
+ if (!registerFn) {
1341
+ throw new Error('Registry must have a registerTool or register method');
1342
+ }
1343
+ claimsTools.forEach(tool => registerFn.call(registry, tool));
1344
+ return claimsTools.length;
1345
+ }
1346
+ /**
1347
+ * Get claims tools by category
1348
+ *
1349
+ * @param category - Category name: 'core', 'stealing', 'load', or 'additional'
1350
+ * @returns Array of tools in that category
1351
+ */
1352
+ export function getClaimsToolsByCategory(category) {
1353
+ switch (category) {
1354
+ case 'core':
1355
+ return coreClaimingTools;
1356
+ case 'stealing':
1357
+ return workStealingTools;
1358
+ case 'load':
1359
+ return loadBalancingTools;
1360
+ case 'additional':
1361
+ return additionalClaimsTools;
1362
+ default:
1363
+ return [];
1364
+ }
1365
+ }
1366
+ /**
1367
+ * Get a specific claims tool by name
1368
+ *
1369
+ * @param name - Tool name (e.g., 'claims/issue_claim')
1370
+ * @returns The tool if found, undefined otherwise
1371
+ */
1372
+ export function getClaimsToolByName(name) {
1373
+ return claimsTools.find(tool => tool.name === name);
1374
+ }
1375
+ // ============================================================================
1376
+ // Default Export
1377
+ // ============================================================================
1378
+ export default {
1379
+ // All tools
1380
+ claimsTools,
1381
+ // Tool categories
1382
+ coreClaimingTools,
1383
+ workStealingTools,
1384
+ loadBalancingTools,
1385
+ additionalClaimsTools,
1386
+ // Individual tools
1387
+ issueClaimTool,
1388
+ issueReleaseTool,
1389
+ issueHandoffTool,
1390
+ issueStatusUpdateTool,
1391
+ issueListAvailableTool,
1392
+ issueListMineTool,
1393
+ issueBoardTool,
1394
+ issueMarkStealableTool,
1395
+ issueStealTool,
1396
+ issueGetStealableTool,
1397
+ issueContestStealTool,
1398
+ agentLoadInfoTool,
1399
+ swarmRebalanceTool,
1400
+ swarmLoadOverviewTool,
1401
+ claimHistoryTool,
1402
+ claimMetricsTool,
1403
+ claimConfigTool,
1404
+ // Utility functions
1405
+ registerClaimsTools,
1406
+ getClaimsToolsByCategory,
1407
+ getClaimsToolByName,
1408
+ };
1409
+ //# sourceMappingURL=mcp-tools.js.map