ruvnet-kb-first 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +674 -0
  3. package/SKILL.md +740 -0
  4. package/bin/kb-first.js +123 -0
  5. package/install/init-project.sh +435 -0
  6. package/install/install-global.sh +257 -0
  7. package/install/kb-first-autodetect.sh +108 -0
  8. package/install/kb-first-command.md +80 -0
  9. package/install/kb-first-skill.md +262 -0
  10. package/package.json +87 -0
  11. package/phases/00-assessment.md +529 -0
  12. package/phases/01-storage.md +194 -0
  13. package/phases/01.5-hooks-setup.md +521 -0
  14. package/phases/02-kb-creation.md +413 -0
  15. package/phases/03-persistence.md +125 -0
  16. package/phases/04-visualization.md +170 -0
  17. package/phases/05-integration.md +114 -0
  18. package/phases/06-scaffold.md +130 -0
  19. package/phases/07-build.md +493 -0
  20. package/phases/08-verification.md +597 -0
  21. package/phases/09-security.md +512 -0
  22. package/phases/10-documentation.md +613 -0
  23. package/phases/11-deployment.md +670 -0
  24. package/phases/testing.md +713 -0
  25. package/scripts/1.5-hooks-verify.sh +252 -0
  26. package/scripts/8.1-code-scan.sh +58 -0
  27. package/scripts/8.2-import-check.sh +42 -0
  28. package/scripts/8.3-source-returns.sh +52 -0
  29. package/scripts/8.4-startup-verify.sh +65 -0
  30. package/scripts/8.5-fallback-check.sh +63 -0
  31. package/scripts/8.6-attribution.sh +56 -0
  32. package/scripts/8.7-confidence.sh +56 -0
  33. package/scripts/8.8-gap-logging.sh +70 -0
  34. package/scripts/9-security-audit.sh +202 -0
  35. package/scripts/init-project.sh +395 -0
  36. package/scripts/verify-enforcement.sh +167 -0
  37. package/src/commands/hooks.js +361 -0
  38. package/src/commands/init.js +315 -0
  39. package/src/commands/phase.js +372 -0
  40. package/src/commands/score.js +380 -0
  41. package/src/commands/status.js +193 -0
  42. package/src/commands/verify.js +286 -0
  43. package/src/index.js +56 -0
  44. package/src/mcp-server.js +412 -0
  45. package/templates/attention-router.ts +534 -0
  46. package/templates/code-analysis.ts +683 -0
  47. package/templates/federated-kb-learner.ts +649 -0
  48. package/templates/gnn-engine.ts +1091 -0
  49. package/templates/intentions.md +277 -0
  50. package/templates/kb-client.ts +905 -0
  51. package/templates/schema.sql +303 -0
  52. package/templates/sona-config.ts +312 -0
@@ -0,0 +1,649 @@
1
+ /**
2
+ * KB-First v3.0 - Federated Learning Template
3
+ *
4
+ * Distributed expert knowledge curation using agentic-flow's
5
+ * FederatedLearningCoordinator and EphemeralLearningAgent.
6
+ *
7
+ * Use this for:
8
+ * - Multi-expert knowledge aggregation
9
+ * - Quality-gated KB ingestion
10
+ * - Distributed learning without centralizing data
11
+ */
12
+
13
+ // =============================================================================
14
+ // TYPES
15
+ // =============================================================================
16
+
17
+ export interface ExpertAgent {
18
+ id: string;
19
+ domain: string;
20
+ expertise: string[];
21
+ qualityThreshold: number;
22
+ learningRate: number;
23
+ contributionCount: number;
24
+ }
25
+
26
+ export interface KnowledgeContribution {
27
+ agentId: string;
28
+ domain: string;
29
+ title: string;
30
+ content: string;
31
+ sourceUrl: string;
32
+ confidence: number;
33
+ metadata: Record<string, unknown>;
34
+ qualityScore: number;
35
+ }
36
+
37
+ export interface AggregationResult {
38
+ accepted: KnowledgeContribution[];
39
+ rejected: KnowledgeContribution[];
40
+ aggregatedConfidence: number;
41
+ qualityMetrics: {
42
+ avgQuality: number;
43
+ minQuality: number;
44
+ maxQuality: number;
45
+ consensusLevel: number;
46
+ };
47
+ }
48
+
49
+ export interface FederatedConfig {
50
+ // Quality gating
51
+ qualityThreshold: number; // Min quality to accept (0.6-0.9)
52
+ consensusRequired: number; // Number of agents that must agree (1-5)
53
+
54
+ // Aggregation strategy
55
+ aggregationStrategy: 'weighted' | 'majority' | 'quality_first' | 'expert_weighted';
56
+ weightByExpertise: boolean;
57
+
58
+ // Scale limits
59
+ maxAgents: number;
60
+ maxContributionsPerRound: number;
61
+
62
+ // Learning parameters
63
+ learningRate: number;
64
+ adaptiveThreshold: boolean; // Adjust threshold based on domain
65
+ }
66
+
67
+ // =============================================================================
68
+ // DEFAULT CONFIGURATION
69
+ // =============================================================================
70
+
71
+ const DEFAULT_CONFIG: FederatedConfig = {
72
+ qualityThreshold: 0.7,
73
+ consensusRequired: 2,
74
+ aggregationStrategy: 'quality_first',
75
+ weightByExpertise: true,
76
+ maxAgents: 50,
77
+ maxContributionsPerRound: 100,
78
+ learningRate: 0.1,
79
+ adaptiveThreshold: true
80
+ };
81
+
82
+ // =============================================================================
83
+ // FEDERATED KB COORDINATOR
84
+ // =============================================================================
85
+
86
+ export class FederatedKBCoordinator {
87
+ private config: FederatedConfig;
88
+ private agents: Map<string, ExpertAgent>;
89
+ private pendingContributions: KnowledgeContribution[];
90
+ private domainThresholds: Map<string, number>;
91
+
92
+ // Integration with agentic-flow (lazy loaded)
93
+ private agenticCoordinator: any = null;
94
+
95
+ constructor(config: Partial<FederatedConfig> = {}) {
96
+ this.config = { ...DEFAULT_CONFIG, ...config };
97
+ this.agents = new Map();
98
+ this.pendingContributions = [];
99
+ this.domainThresholds = new Map();
100
+ }
101
+
102
+ // ---------------------------------------------------------------------------
103
+ // INITIALIZATION
104
+ // ---------------------------------------------------------------------------
105
+
106
+ /**
107
+ * Initialize connection to agentic-flow's FederatedLearningCoordinator
108
+ */
109
+ async initialize(): Promise<boolean> {
110
+ try {
111
+ const agenticFlow = await import('agentic-flow');
112
+
113
+ if (agenticFlow.FederatedLearningCoordinator) {
114
+ this.agenticCoordinator = new agenticFlow.FederatedLearningCoordinator({
115
+ qualityThreshold: this.config.qualityThreshold,
116
+ aggregationStrategy: this.config.aggregationStrategy,
117
+ maxAgents: this.config.maxAgents
118
+ });
119
+
120
+ console.log('[FEDERATED-KB] Initialized with agentic-flow coordinator');
121
+ return true;
122
+ }
123
+
124
+ console.log('[FEDERATED-KB] Running in standalone mode');
125
+ return false;
126
+ } catch (e) {
127
+ console.log('[FEDERATED-KB] agentic-flow not available, using standalone mode');
128
+ return false;
129
+ }
130
+ }
131
+
132
+ // ---------------------------------------------------------------------------
133
+ // AGENT MANAGEMENT
134
+ // ---------------------------------------------------------------------------
135
+
136
+ /**
137
+ * Register an expert agent for knowledge contribution
138
+ */
139
+ registerAgent(agent: Omit<ExpertAgent, 'contributionCount'>): ExpertAgent {
140
+ const fullAgent: ExpertAgent = {
141
+ ...agent,
142
+ contributionCount: 0
143
+ };
144
+
145
+ this.agents.set(agent.id, fullAgent);
146
+
147
+ // Set domain-specific threshold if adaptive
148
+ if (this.config.adaptiveThreshold && !this.domainThresholds.has(agent.domain)) {
149
+ this.domainThresholds.set(agent.domain, this.config.qualityThreshold);
150
+ }
151
+
152
+ console.log(`[FEDERATED-KB] Registered agent: ${agent.id} (domain: ${agent.domain})`);
153
+ return fullAgent;
154
+ }
155
+
156
+ /**
157
+ * Create an ephemeral learning agent for temporary tasks
158
+ */
159
+ async createEphemeralAgent(
160
+ domain: string,
161
+ task: string,
162
+ options: {
163
+ expertise?: string[];
164
+ qualityThreshold?: number;
165
+ ttlMinutes?: number;
166
+ } = {}
167
+ ): Promise<ExpertAgent> {
168
+ const agentId = `ephemeral-${domain}-${Date.now()}`;
169
+
170
+ const agent = this.registerAgent({
171
+ id: agentId,
172
+ domain,
173
+ expertise: options.expertise || [domain],
174
+ qualityThreshold: options.qualityThreshold || this.config.qualityThreshold,
175
+ learningRate: this.config.learningRate
176
+ });
177
+
178
+ // If agentic-flow is available, create a real ephemeral agent
179
+ if (this.agenticCoordinator) {
180
+ try {
181
+ const agenticFlow = await import('agentic-flow');
182
+ if (agenticFlow.EphemeralLearningAgent) {
183
+ const ephemeralAgent = new agenticFlow.EphemeralLearningAgent({
184
+ id: agentId,
185
+ domain,
186
+ task,
187
+ ttlMinutes: options.ttlMinutes || 30,
188
+ contributeTo: this.agenticCoordinator
189
+ });
190
+
191
+ console.log(`[FEDERATED-KB] Created ephemeral agent with agentic-flow: ${agentId}`);
192
+ }
193
+ } catch (e) {
194
+ // Standalone mode
195
+ }
196
+ }
197
+
198
+ return agent;
199
+ }
200
+
201
+ /**
202
+ * Get all registered agents
203
+ */
204
+ getAgents(domain?: string): ExpertAgent[] {
205
+ const agents = [...this.agents.values()];
206
+ return domain ? agents.filter(a => a.domain === domain) : agents;
207
+ }
208
+
209
+ // ---------------------------------------------------------------------------
210
+ // CONTRIBUTION SUBMISSION
211
+ // ---------------------------------------------------------------------------
212
+
213
+ /**
214
+ * Submit a knowledge contribution from an agent
215
+ */
216
+ async submitContribution(
217
+ agentId: string,
218
+ contribution: Omit<KnowledgeContribution, 'agentId' | 'qualityScore'>
219
+ ): Promise<{ accepted: boolean; qualityScore: number; reason?: string }> {
220
+ const agent = this.agents.get(agentId);
221
+ if (!agent) {
222
+ return { accepted: false, qualityScore: 0, reason: 'Agent not registered' };
223
+ }
224
+
225
+ // Calculate quality score
226
+ const qualityScore = this.calculateQualityScore(contribution, agent);
227
+
228
+ const fullContribution: KnowledgeContribution = {
229
+ ...contribution,
230
+ agentId,
231
+ qualityScore
232
+ };
233
+
234
+ // Check against threshold
235
+ const threshold = this.config.adaptiveThreshold
236
+ ? this.domainThresholds.get(contribution.domain) || this.config.qualityThreshold
237
+ : this.config.qualityThreshold;
238
+
239
+ if (qualityScore < threshold) {
240
+ return {
241
+ accepted: false,
242
+ qualityScore,
243
+ reason: `Quality score ${qualityScore.toFixed(2)} below threshold ${threshold}`
244
+ };
245
+ }
246
+
247
+ // Add to pending contributions
248
+ this.pendingContributions.push(fullContribution);
249
+ agent.contributionCount++;
250
+
251
+ // Auto-aggregate if we have enough contributions
252
+ if (this.pendingContributions.length >= this.config.maxContributionsPerRound) {
253
+ await this.aggregateContributions();
254
+ }
255
+
256
+ return { accepted: true, qualityScore };
257
+ }
258
+
259
+ /**
260
+ * Calculate quality score for a contribution
261
+ */
262
+ private calculateQualityScore(
263
+ contribution: Omit<KnowledgeContribution, 'agentId' | 'qualityScore'>,
264
+ agent: ExpertAgent
265
+ ): number {
266
+ let score = contribution.confidence;
267
+
268
+ // Weight by agent's expertise match
269
+ if (this.config.weightByExpertise) {
270
+ const expertiseMatch = agent.expertise.some(e =>
271
+ contribution.domain.toLowerCase().includes(e.toLowerCase()) ||
272
+ e.toLowerCase().includes(contribution.domain.toLowerCase())
273
+ );
274
+ if (expertiseMatch) {
275
+ score *= 1.2; // 20% boost for expertise match
276
+ }
277
+ }
278
+
279
+ // Content quality factors
280
+ const contentLength = contribution.content.length;
281
+ if (contentLength > 500 && contentLength < 10000) {
282
+ score *= 1.1; // Boost for good content length
283
+ }
284
+ if (contentLength < 100) {
285
+ score *= 0.7; // Penalty for too short
286
+ }
287
+ if (contentLength > 20000) {
288
+ score *= 0.9; // Slight penalty for too long
289
+ }
290
+
291
+ // Source URL bonus
292
+ if (contribution.sourceUrl && contribution.sourceUrl.startsWith('https://')) {
293
+ score *= 1.05;
294
+ }
295
+
296
+ // Normalize to 0-1 range
297
+ return Math.min(1, Math.max(0, score));
298
+ }
299
+
300
+ // ---------------------------------------------------------------------------
301
+ // AGGREGATION
302
+ // ---------------------------------------------------------------------------
303
+
304
+ /**
305
+ * Aggregate pending contributions and ingest to KB
306
+ */
307
+ async aggregateContributions(): Promise<AggregationResult> {
308
+ const contributions = [...this.pendingContributions];
309
+ this.pendingContributions = [];
310
+
311
+ if (contributions.length === 0) {
312
+ return {
313
+ accepted: [],
314
+ rejected: [],
315
+ aggregatedConfidence: 0,
316
+ qualityMetrics: { avgQuality: 0, minQuality: 0, maxQuality: 0, consensusLevel: 0 }
317
+ };
318
+ }
319
+
320
+ // Group by domain for consensus checking
321
+ const byDomain = new Map<string, KnowledgeContribution[]>();
322
+ contributions.forEach(c => {
323
+ const list = byDomain.get(c.domain) || [];
324
+ list.push(c);
325
+ byDomain.set(c.domain, list);
326
+ });
327
+
328
+ const accepted: KnowledgeContribution[] = [];
329
+ const rejected: KnowledgeContribution[] = [];
330
+
331
+ // Apply aggregation strategy
332
+ for (const [domain, domainContributions] of byDomain) {
333
+ const { pass, fail } = this.applyAggregationStrategy(domainContributions);
334
+ accepted.push(...pass);
335
+ rejected.push(...fail);
336
+ }
337
+
338
+ // Calculate quality metrics
339
+ const qualities = accepted.map(c => c.qualityScore);
340
+ const qualityMetrics = {
341
+ avgQuality: qualities.length > 0 ? qualities.reduce((a, b) => a + b, 0) / qualities.length : 0,
342
+ minQuality: qualities.length > 0 ? Math.min(...qualities) : 0,
343
+ maxQuality: qualities.length > 0 ? Math.max(...qualities) : 0,
344
+ consensusLevel: accepted.length / (accepted.length + rejected.length) || 0
345
+ };
346
+
347
+ // Update adaptive thresholds based on quality metrics
348
+ if (this.config.adaptiveThreshold) {
349
+ this.updateAdaptiveThresholds(accepted);
350
+ }
351
+
352
+ // Ingest accepted contributions to KB
353
+ if (accepted.length > 0) {
354
+ await this.ingestToKB(accepted);
355
+ }
356
+
357
+ console.log(`[FEDERATED-KB] Aggregation complete: ${accepted.length} accepted, ${rejected.length} rejected`);
358
+
359
+ return {
360
+ accepted,
361
+ rejected,
362
+ aggregatedConfidence: qualityMetrics.avgQuality,
363
+ qualityMetrics
364
+ };
365
+ }
366
+
367
+ /**
368
+ * Apply the configured aggregation strategy
369
+ */
370
+ private applyAggregationStrategy(contributions: KnowledgeContribution[]): {
371
+ pass: KnowledgeContribution[];
372
+ fail: KnowledgeContribution[];
373
+ } {
374
+ const pass: KnowledgeContribution[] = [];
375
+ const fail: KnowledgeContribution[] = [];
376
+
377
+ switch (this.config.aggregationStrategy) {
378
+ case 'quality_first':
379
+ // Sort by quality, take top ones above threshold
380
+ contributions
381
+ .sort((a, b) => b.qualityScore - a.qualityScore)
382
+ .forEach(c => {
383
+ if (c.qualityScore >= this.config.qualityThreshold) {
384
+ pass.push(c);
385
+ } else {
386
+ fail.push(c);
387
+ }
388
+ });
389
+ break;
390
+
391
+ case 'weighted':
392
+ // Weight by agent expertise and quality
393
+ contributions.forEach(c => {
394
+ const agent = this.agents.get(c.agentId);
395
+ const expertiseBonus = agent?.expertise.length ? 0.1 * agent.expertise.length : 0;
396
+ const weightedScore = c.qualityScore + expertiseBonus;
397
+ if (weightedScore >= this.config.qualityThreshold) {
398
+ pass.push(c);
399
+ } else {
400
+ fail.push(c);
401
+ }
402
+ });
403
+ break;
404
+
405
+ case 'majority':
406
+ // Group similar contributions and require majority agreement
407
+ const grouped = this.groupSimilarContributions(contributions);
408
+ grouped.forEach(group => {
409
+ if (group.length >= this.config.consensusRequired) {
410
+ // Take the highest quality one from the group
411
+ const best = group.sort((a, b) => b.qualityScore - a.qualityScore)[0];
412
+ pass.push(best);
413
+ group.slice(1).forEach(c => fail.push(c));
414
+ } else {
415
+ group.forEach(c => fail.push(c));
416
+ }
417
+ });
418
+ break;
419
+
420
+ case 'expert_weighted':
421
+ // Weight heavily by agent contribution history
422
+ contributions.forEach(c => {
423
+ const agent = this.agents.get(c.agentId);
424
+ const contributionBonus = Math.min(0.2, (agent?.contributionCount || 0) * 0.01);
425
+ const weightedScore = c.qualityScore + contributionBonus;
426
+ if (weightedScore >= this.config.qualityThreshold) {
427
+ pass.push(c);
428
+ } else {
429
+ fail.push(c);
430
+ }
431
+ });
432
+ break;
433
+ }
434
+
435
+ return { pass, fail };
436
+ }
437
+
438
+ /**
439
+ * Group similar contributions for consensus checking
440
+ */
441
+ private groupSimilarContributions(contributions: KnowledgeContribution[]): KnowledgeContribution[][] {
442
+ // Simple title-based grouping (could be enhanced with embeddings)
443
+ const groups: KnowledgeContribution[][] = [];
444
+ const used = new Set<number>();
445
+
446
+ contributions.forEach((c, i) => {
447
+ if (used.has(i)) return;
448
+
449
+ const group = [c];
450
+ used.add(i);
451
+
452
+ contributions.forEach((other, j) => {
453
+ if (i === j || used.has(j)) return;
454
+
455
+ // Simple similarity check (could use embeddings for better matching)
456
+ const titleSimilar = this.stringSimilarity(c.title, other.title) > 0.7;
457
+ const domainMatch = c.domain === other.domain;
458
+
459
+ if (titleSimilar && domainMatch) {
460
+ group.push(other);
461
+ used.add(j);
462
+ }
463
+ });
464
+
465
+ groups.push(group);
466
+ });
467
+
468
+ return groups;
469
+ }
470
+
471
+ /**
472
+ * Simple string similarity (Jaccard index on words)
473
+ */
474
+ private stringSimilarity(a: string, b: string): number {
475
+ const wordsA = new Set(a.toLowerCase().split(/\s+/));
476
+ const wordsB = new Set(b.toLowerCase().split(/\s+/));
477
+ const intersection = new Set([...wordsA].filter(x => wordsB.has(x)));
478
+ const union = new Set([...wordsA, ...wordsB]);
479
+ return intersection.size / union.size;
480
+ }
481
+
482
+ /**
483
+ * Update adaptive thresholds based on domain performance
484
+ */
485
+ private updateAdaptiveThresholds(accepted: KnowledgeContribution[]): void {
486
+ const byDomain = new Map<string, number[]>();
487
+
488
+ accepted.forEach(c => {
489
+ const list = byDomain.get(c.domain) || [];
490
+ list.push(c.qualityScore);
491
+ byDomain.set(c.domain, list);
492
+ });
493
+
494
+ byDomain.forEach((scores, domain) => {
495
+ const avg = scores.reduce((a, b) => a + b, 0) / scores.length;
496
+ const currentThreshold = this.domainThresholds.get(domain) || this.config.qualityThreshold;
497
+
498
+ // Slowly adapt threshold toward observed quality
499
+ const newThreshold = currentThreshold + this.config.learningRate * (avg - currentThreshold);
500
+ this.domainThresholds.set(domain, Math.max(0.5, Math.min(0.95, newThreshold)));
501
+ });
502
+ }
503
+
504
+ /**
505
+ * Ingest accepted contributions to the KB
506
+ */
507
+ private async ingestToKB(contributions: KnowledgeContribution[]): Promise<void> {
508
+ try {
509
+ // Import kb-client dynamically
510
+ const kbClient = await import('./kb-client');
511
+
512
+ for (const contribution of contributions) {
513
+ await kbClient.ingestDocument(
514
+ contribution.domain,
515
+ `federated/${contribution.agentId}/${contribution.title.replace(/[^a-z0-9]/gi, '-').toLowerCase()}`,
516
+ contribution.title,
517
+ contribution.content,
518
+ {
519
+ sourceExpert: `federated:${contribution.agentId}`,
520
+ sourceUrl: contribution.sourceUrl,
521
+ confidence: contribution.qualityScore,
522
+ metadata: {
523
+ ...contribution.metadata,
524
+ federatedAgent: contribution.agentId,
525
+ aggregatedAt: new Date().toISOString()
526
+ }
527
+ }
528
+ );
529
+ }
530
+
531
+ console.log(`[FEDERATED-KB] Ingested ${contributions.length} contributions to KB`);
532
+ } catch (e) {
533
+ console.error('[FEDERATED-KB] KB ingestion failed:', e);
534
+ }
535
+ }
536
+
537
+ // ---------------------------------------------------------------------------
538
+ // STATISTICS & MONITORING
539
+ // ---------------------------------------------------------------------------
540
+
541
+ /**
542
+ * Get coordinator statistics
543
+ */
544
+ getStats(): {
545
+ totalAgents: number;
546
+ pendingContributions: number;
547
+ domainThresholds: Record<string, number>;
548
+ topContributors: Array<{ id: string; domain: string; contributions: number }>;
549
+ } {
550
+ const agents = [...this.agents.values()];
551
+
552
+ return {
553
+ totalAgents: agents.length,
554
+ pendingContributions: this.pendingContributions.length,
555
+ domainThresholds: Object.fromEntries(this.domainThresholds),
556
+ topContributors: agents
557
+ .sort((a, b) => b.contributionCount - a.contributionCount)
558
+ .slice(0, 10)
559
+ .map(a => ({ id: a.id, domain: a.domain, contributions: a.contributionCount }))
560
+ };
561
+ }
562
+ }
563
+
564
+ // =============================================================================
565
+ // PRESET CONFIGURATIONS
566
+ // =============================================================================
567
+
568
+ export const PRESETS = {
569
+ /**
570
+ * Strict quality gating for critical domains (medical, legal, finance)
571
+ */
572
+ strict: {
573
+ qualityThreshold: 0.85,
574
+ consensusRequired: 3,
575
+ aggregationStrategy: 'majority' as const,
576
+ weightByExpertise: true,
577
+ maxAgents: 20,
578
+ maxContributionsPerRound: 50,
579
+ learningRate: 0.05,
580
+ adaptiveThreshold: false
581
+ },
582
+
583
+ /**
584
+ * Balanced for general knowledge domains
585
+ */
586
+ balanced: {
587
+ qualityThreshold: 0.7,
588
+ consensusRequired: 2,
589
+ aggregationStrategy: 'quality_first' as const,
590
+ weightByExpertise: true,
591
+ maxAgents: 50,
592
+ maxContributionsPerRound: 100,
593
+ learningRate: 0.1,
594
+ adaptiveThreshold: true
595
+ },
596
+
597
+ /**
598
+ * Permissive for rapidly evolving domains (tech, news)
599
+ */
600
+ permissive: {
601
+ qualityThreshold: 0.5,
602
+ consensusRequired: 1,
603
+ aggregationStrategy: 'weighted' as const,
604
+ weightByExpertise: false,
605
+ maxAgents: 100,
606
+ maxContributionsPerRound: 200,
607
+ learningRate: 0.2,
608
+ adaptiveThreshold: true
609
+ },
610
+
611
+ /**
612
+ * Expert-focused for specialized domains
613
+ */
614
+ expertFocused: {
615
+ qualityThreshold: 0.75,
616
+ consensusRequired: 2,
617
+ aggregationStrategy: 'expert_weighted' as const,
618
+ weightByExpertise: true,
619
+ maxAgents: 30,
620
+ maxContributionsPerRound: 75,
621
+ learningRate: 0.1,
622
+ adaptiveThreshold: true
623
+ }
624
+ };
625
+
626
+ // =============================================================================
627
+ // CONVENIENCE FACTORY
628
+ // =============================================================================
629
+
630
+ /**
631
+ * Create a federated coordinator with a preset configuration
632
+ */
633
+ export function createFederatedCoordinator(
634
+ preset: keyof typeof PRESETS = 'balanced',
635
+ overrides: Partial<FederatedConfig> = {}
636
+ ): FederatedKBCoordinator {
637
+ const config = { ...PRESETS[preset], ...overrides };
638
+ return new FederatedKBCoordinator(config);
639
+ }
640
+
641
+ // =============================================================================
642
+ // EXPORTS
643
+ // =============================================================================
644
+
645
+ export default {
646
+ FederatedKBCoordinator,
647
+ createFederatedCoordinator,
648
+ PRESETS
649
+ };