@kernel.chat/kbot 3.94.0 → 3.97.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.
@@ -0,0 +1,682 @@
1
+ // kbot Intelligence Coordinator — Unified Brain
2
+ //
3
+ // Orchestrates kbot's 14 intelligence systems into a coherent cognitive loop.
4
+ // Called before, during, and after each agent interaction.
5
+ //
6
+ // All module imports are LAZY (dynamic) to avoid circular dependency issues.
7
+ // State persists to ~/.kbot/coordinator-state.json.
8
+ import { homedir } from 'node:os';
9
+ import { join } from 'node:path';
10
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
11
+ // ── Lazy module loaders (dynamic imports to break circular deps) ──
12
+ async function getLearning() { return import('./learning.js'); }
13
+ async function getConfidence() { return import('./confidence.js'); }
14
+ async function getReasoning() { return import('./reasoning.js'); }
15
+ async function getGraphMemory() { return import('./graph-memory.js'); }
16
+ async function getLearnedRouter() { return import('./learned-router.js'); }
17
+ async function getIntentionality() { return import('./intentionality.js'); }
18
+ async function getTemporal() { return import('./temporal.js'); }
19
+ async function getBehaviour() { return import('./behaviour.js'); }
20
+ async function getEmergent() {
21
+ try {
22
+ // emergent.ts doesn't exist yet — will be created when emergent module is built
23
+ // Using indirect import to prevent TypeScript from resolving at compile time
24
+ const path = './tools/emergent' + '.js';
25
+ return await import(/* @vite-ignore */ path);
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ }
31
+ // ── Defaults ──
32
+ const DEFAULT_CONFIDENCE_THRESHOLD = 0.4;
33
+ const CONSOLIDATION_INTERVAL = 10; // every N interactions
34
+ const MAX_EVAL_HISTORY = 200;
35
+ const MAX_INSIGHTS = 100;
36
+ const MAX_CONFLICTS = 50;
37
+ const MAX_GOALS = 20;
38
+ function defaultState() {
39
+ return {
40
+ lastPolicy: 'balanced',
41
+ confidenceThreshold: DEFAULT_CONFIDENCE_THRESHOLD,
42
+ totalInteractions: 0,
43
+ successRate: 0.5,
44
+ activeGoals: [],
45
+ recentInsights: [],
46
+ conflictLog: [],
47
+ evalHistory: [],
48
+ patternsLearnedToday: 0,
49
+ patternsLearnedDate: new Date().toISOString().slice(0, 10),
50
+ routingAccuracy: 0.5,
51
+ lastConsolidation: null,
52
+ startedAt: new Date().toISOString(),
53
+ };
54
+ }
55
+ // ── Persistence helpers ──
56
+ const KBOT_DIR = join(homedir(), '.kbot');
57
+ const STATE_FILE = join(KBOT_DIR, 'coordinator-state.json');
58
+ function ensureDir() {
59
+ if (!existsSync(KBOT_DIR))
60
+ mkdirSync(KBOT_DIR, { recursive: true });
61
+ }
62
+ function loadState() {
63
+ ensureDir();
64
+ if (!existsSync(STATE_FILE))
65
+ return defaultState();
66
+ try {
67
+ return { ...defaultState(), ...JSON.parse(readFileSync(STATE_FILE, 'utf-8')) };
68
+ }
69
+ catch {
70
+ return defaultState();
71
+ }
72
+ }
73
+ function saveState(state) {
74
+ ensureDir();
75
+ try {
76
+ writeFileSync(STATE_FILE, JSON.stringify(state, null, 2), 'utf-8');
77
+ }
78
+ catch {
79
+ // best-effort — coordinator state can be regenerated
80
+ }
81
+ }
82
+ // ── Utility ──
83
+ function shortHash(s) {
84
+ let hash = 0;
85
+ for (let i = 0; i < s.length; i++) {
86
+ hash = ((hash << 5) - hash + s.charCodeAt(i)) | 0;
87
+ }
88
+ return Math.abs(hash).toString(36).slice(0, 8);
89
+ }
90
+ function shortId() {
91
+ return Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
92
+ }
93
+ function todayStr() {
94
+ return new Date().toISOString().slice(0, 10);
95
+ }
96
+ // ── IntelligenceCoordinator ──
97
+ export class IntelligenceCoordinator {
98
+ state;
99
+ anticipatedTools = [];
100
+ currentSessionId = null;
101
+ pendingRouteAgent = null;
102
+ initTime = Date.now();
103
+ constructor() {
104
+ this.state = loadState();
105
+ // Reset daily pattern counter if it's a new day
106
+ if (this.state.patternsLearnedDate !== todayStr()) {
107
+ this.state.patternsLearnedToday = 0;
108
+ this.state.patternsLearnedDate = todayStr();
109
+ }
110
+ }
111
+ // ── Phase 1: Pre-Execution (before LLM API call) ──
112
+ async preProcess(message, sessionId) {
113
+ this.currentSessionId = sessionId;
114
+ this.state.totalInteractions++;
115
+ const result = {
116
+ agent: null,
117
+ confidence: 0.5,
118
+ graphContext: '',
119
+ reasoning: '',
120
+ toolHints: [],
121
+ systemPromptAddition: '',
122
+ needsClarification: false,
123
+ drives: null,
124
+ anticipation: null,
125
+ };
126
+ // 1. Route message through learned-router
127
+ try {
128
+ const router = await getLearnedRouter();
129
+ const route = router.learnedRoute(message);
130
+ if (route) {
131
+ result.agent = route.agent;
132
+ result.confidence = route.confidence;
133
+ this.pendingRouteAgent = route.agent;
134
+ }
135
+ }
136
+ catch { /* non-critical */ }
137
+ // 2. Check confidence — if below threshold, flag for clarification
138
+ try {
139
+ const conf = await getConfidence();
140
+ const score = conf.estimateConfidence(message, '');
141
+ result.confidence = Math.max(result.confidence, score.overall);
142
+ if (score.overall < this.state.confidenceThreshold) {
143
+ result.needsClarification = true;
144
+ result.clarificationReason = score.reasoning || 'Low confidence — consider asking for more details';
145
+ }
146
+ }
147
+ catch { /* non-critical */ }
148
+ // 3. Query graph-memory for relevant context
149
+ try {
150
+ const graph = await getGraphMemory();
151
+ const contextStr = graph.toContext(1500);
152
+ if (contextStr && contextStr.length > 10) {
153
+ result.graphContext = contextStr;
154
+ }
155
+ // Also find nodes related to message keywords
156
+ const found = graph.findNode(message.slice(0, 100));
157
+ if (found.length > 0) {
158
+ const entityNames = found.slice(0, 5).map(n => n.name).join(', ');
159
+ result.graphContext += `\nRelated entities: ${entityNames}`;
160
+ }
161
+ }
162
+ catch { /* non-critical */ }
163
+ // 4. Run abductive reasoning to infer intent
164
+ try {
165
+ const reasoning = await getReasoning();
166
+ const strategy = reasoning.selectStrategy(message, '');
167
+ result.reasoning = strategy.reasoning || strategy.chosenStrategy;
168
+ }
169
+ catch { /* non-critical */ }
170
+ // 5. Check intentionality drives
171
+ try {
172
+ const intent = await getIntentionality();
173
+ const drives = intent.getDriveState();
174
+ if (drives && drives.drives && drives.drives.length > 0) {
175
+ const top = drives.drives.reduce((a, b) => (b.weight * (1 - b.currentSatisfaction)) > (a.weight * (1 - a.currentSatisfaction)) ? b : a);
176
+ result.drives = { dominant: top.name, level: top.weight };
177
+ }
178
+ }
179
+ catch { /* non-critical */ }
180
+ // 6. Anticipate what tools will be needed (temporal)
181
+ try {
182
+ const temporal = await getTemporal();
183
+ const anticipated = temporal.anticipateNext([message], message);
184
+ if (anticipated && anticipated.length > 0 && anticipated[0].prediction) {
185
+ result.anticipation = anticipated[0].prediction;
186
+ result.toolHints = anticipated[0].preparation || [];
187
+ this.anticipatedTools = result.toolHints;
188
+ }
189
+ }
190
+ catch { /* non-critical */ }
191
+ // 7. Load behaviour rules for system prompt
192
+ try {
193
+ const behaviour = await getBehaviour();
194
+ const prompt = behaviour.getBehaviourPrompt();
195
+ if (prompt && prompt.length > 5) {
196
+ result.systemPromptAddition = prompt;
197
+ }
198
+ }
199
+ catch { /* non-critical */ }
200
+ // 8. Synthesize policy from signals
201
+ this.state.lastPolicy = this.synthesizePolicy(result.confidence);
202
+ // Add policy hint to system prompt
203
+ if (this.state.lastPolicy === 'explore') {
204
+ result.systemPromptAddition += '\n\nNote: Consider unconventional approaches. The user may benefit from a different perspective.';
205
+ }
206
+ else if (this.state.lastPolicy === 'exploit') {
207
+ result.systemPromptAddition += '\n\nNote: Use the most reliable, proven approach. The user needs a direct solution.';
208
+ }
209
+ // Trim empty lines from system prompt addition
210
+ result.systemPromptAddition = result.systemPromptAddition.trim();
211
+ this.save();
212
+ return result;
213
+ }
214
+ // ── Phase 2: Tool Oversight (before each tool execution) ──
215
+ evaluateToolCall(toolName, args, _context) {
216
+ const evaluation = {
217
+ allow: true,
218
+ anticipated: false,
219
+ };
220
+ // 1. Check if tool matches anticipated needs
221
+ if (this.anticipatedTools.length > 0) {
222
+ evaluation.anticipated = this.anticipatedTools.some(hint => toolName.includes(hint) || hint.includes(toolName));
223
+ }
224
+ // 2. Confidence-gate: warn if success rate is low for this tool pattern
225
+ // We check the eval history for similar tool usage patterns
226
+ const recentEvals = this.state.evalHistory.slice(-20);
227
+ if (recentEvals.length >= 5) {
228
+ const avgScore = recentEvals.reduce((s, e) => s + e.score, 0) / recentEvals.length;
229
+ if (avgScore < 0.3) {
230
+ evaluation.warn = `Recent interaction quality is low (${(avgScore * 100).toFixed(0)}%). Consider verifying approach.`;
231
+ }
232
+ }
233
+ // 3. Check behaviour rules for restrictions (synchronous)
234
+ // We do a lightweight check against known restricted patterns
235
+ try {
236
+ // Destructive tool patterns that should trigger caution
237
+ const cautionPatterns = [
238
+ 'delete', 'remove', 'drop', 'destroy', 'force', 'reset',
239
+ 'truncate', 'wipe', 'purge', 'nuke',
240
+ ];
241
+ const toolLower = toolName.toLowerCase();
242
+ const argsStr = JSON.stringify(args).toLowerCase();
243
+ const isDestructive = cautionPatterns.some(p => toolLower.includes(p) || argsStr.includes(p));
244
+ if (isDestructive && !evaluation.warn) {
245
+ evaluation.warn = `Tool "${toolName}" appears destructive. Verify intent.`;
246
+ }
247
+ }
248
+ catch { /* non-critical */ }
249
+ // 4. Log tool usage to graph-memory (async, fire-and-forget)
250
+ this.logToolToGraph(toolName, args).catch(() => { });
251
+ return evaluation;
252
+ }
253
+ // ── Phase 3: Post-Response Self-Evaluation ──
254
+ async postProcess(message, response, toolsUsed, sessionId) {
255
+ const result = {
256
+ score: 0.5,
257
+ patternsExtracted: 0,
258
+ insightsGenerated: 0,
259
+ graphUpdates: 0,
260
+ consolidationTriggered: false,
261
+ };
262
+ // 1. Self-evaluate: was the response helpful? (heuristic, no LLM call)
263
+ result.score = this.selfEvaluate(message, response, toolsUsed);
264
+ // Record the evaluation
265
+ const evalEntry = {
266
+ sessionId,
267
+ messageHash: shortHash(message),
268
+ score: result.score,
269
+ toolSuccessRate: toolsUsed.length > 0 ? result.score : 1,
270
+ responseAppropriate: result.score >= 0.4,
271
+ patternsMatched: 0,
272
+ timestamp: new Date().toISOString(),
273
+ };
274
+ this.state.evalHistory.push(evalEntry);
275
+ if (this.state.evalHistory.length > MAX_EVAL_HISTORY) {
276
+ this.state.evalHistory = this.state.evalHistory.slice(-MAX_EVAL_HISTORY);
277
+ }
278
+ // Update rolling success rate (exponential moving average, alpha=0.1)
279
+ this.state.successRate = this.state.successRate * 0.9 + result.score * 0.1;
280
+ // 2. Extract patterns from successful interaction
281
+ if (result.score >= 0.5) {
282
+ try {
283
+ const learning = await getLearning();
284
+ learning.learnFromExchange(message, response, toolsUsed);
285
+ result.patternsExtracted++;
286
+ this.state.patternsLearnedToday++;
287
+ }
288
+ catch { /* non-critical */ }
289
+ }
290
+ // 3. Record routing outcome
291
+ if (this.pendingRouteAgent) {
292
+ try {
293
+ const router = await getLearnedRouter();
294
+ router.recordRoute(message, this.pendingRouteAgent, 'learned', result.score >= 0.5);
295
+ // Update routing accuracy
296
+ const routerStats = router.getRoutingStats();
297
+ if (routerStats.totalRoutes > 0) {
298
+ this.state.routingAccuracy = routerStats.learnedHits / routerStats.totalRoutes;
299
+ }
300
+ }
301
+ catch { /* non-critical */ }
302
+ this.pendingRouteAgent = null;
303
+ }
304
+ // 4. Update graph-memory with entities from the exchange
305
+ try {
306
+ const graph = await getGraphMemory();
307
+ const entities = graph.extractEntities(message, response);
308
+ result.graphUpdates = entities.length;
309
+ }
310
+ catch { /* non-critical */ }
311
+ // 5. Update confidence calibration
312
+ try {
313
+ const conf = await getConfidence();
314
+ conf.recordCalibration(message, result.score, result.score >= 0.5 ? 1 : 0);
315
+ }
316
+ catch { /* non-critical */ }
317
+ // 6. Update intentionality drives
318
+ try {
319
+ const intent = await getIntentionality();
320
+ intent.updateMotivation({
321
+ type: result.score >= 0.6 ? 'task_success' : result.score >= 0.3 ? 'learned_something' : 'task_failure',
322
+ });
323
+ }
324
+ catch { /* non-critical */ }
325
+ // 7. Check if emergent insights arise
326
+ try {
327
+ const emergent = await getEmergent();
328
+ if (emergent && typeof emergent.synthesizeAcross === 'function') {
329
+ const insights = await emergent.synthesizeAcross(this.state.evalHistory.slice(-10).map(e => e.messageHash));
330
+ if (insights && Array.isArray(insights)) {
331
+ for (const insight of insights.slice(0, 3)) {
332
+ this.addInsight(typeof insight === 'string' ? insight : JSON.stringify(insight), 'emergent', 0.6);
333
+ result.insightsGenerated++;
334
+ }
335
+ }
336
+ }
337
+ }
338
+ catch { /* emergent module may not exist yet */ }
339
+ // 8. Check if consolidation is needed
340
+ if (this.state.totalInteractions % CONSOLIDATION_INTERVAL === 0) {
341
+ result.consolidationTriggered = true;
342
+ // Fire-and-forget — don't block the response
343
+ this.consolidate().catch(() => { });
344
+ }
345
+ this.save();
346
+ return result;
347
+ }
348
+ // ── Phase 4: Cross-Session Learning ──
349
+ async consolidate() {
350
+ const result = {
351
+ patternsConsolidated: 0,
352
+ rulesAdded: 0,
353
+ insightsFound: 0,
354
+ graphPruned: { nodes: 0, edges: 0 },
355
+ routingAccuracy: this.state.routingAccuracy,
356
+ };
357
+ // 1. Run selfTrain() on accumulated patterns
358
+ try {
359
+ const learning = await getLearning();
360
+ const trained = learning.selfTrain();
361
+ result.patternsConsolidated = trained.optimized ?? 0;
362
+ }
363
+ catch { /* non-critical */ }
364
+ // 2. Prune weak graph edges
365
+ try {
366
+ const graph = await getGraphMemory();
367
+ // Decay nodes unused for 30 days
368
+ graph.decayUnused(30);
369
+ // Prune very weak connections
370
+ const pruned = graph.prune(0.1);
371
+ result.graphPruned = { nodes: pruned.removedNodes, edges: pruned.removedEdges };
372
+ graph.save();
373
+ }
374
+ catch { /* non-critical */ }
375
+ // 3. Derive behaviour rules from recurring patterns
376
+ try {
377
+ const learning = await getLearning();
378
+ const topPatterns = learning.getTopPatterns(5);
379
+ const behaviour = await getBehaviour();
380
+ for (const pattern of topPatterns) {
381
+ // If a pattern has been used many times, it might warrant a behaviour rule
382
+ if (pattern.hits >= 10 && pattern.successRate >= 0.8) {
383
+ const ruleText = `When asked about "${pattern.intent}", prefer tools: ${pattern.toolSequence.join(', ')}`;
384
+ const added = behaviour.learnGeneral(ruleText);
385
+ if (added)
386
+ result.rulesAdded++;
387
+ }
388
+ }
389
+ }
390
+ catch { /* non-critical */ }
391
+ // 4. Run emergent synthesis
392
+ try {
393
+ const emergent = await getEmergent();
394
+ if (emergent && typeof emergent.consolidate === 'function') {
395
+ const consolidated = await emergent.consolidate();
396
+ if (consolidated && typeof consolidated.insights === 'number') {
397
+ result.insightsFound = consolidated.insights;
398
+ }
399
+ }
400
+ }
401
+ catch { /* emergent module may not exist yet */ }
402
+ // 5. Update routing weights from outcome history
403
+ try {
404
+ const router = await getLearnedRouter();
405
+ const stats = router.getRoutingStats();
406
+ if (stats.totalRoutes > 0) {
407
+ result.routingAccuracy = stats.learnedHits / stats.totalRoutes;
408
+ this.state.routingAccuracy = result.routingAccuracy;
409
+ }
410
+ }
411
+ catch { /* non-critical */ }
412
+ this.state.lastConsolidation = new Date().toISOString();
413
+ this.save();
414
+ return result;
415
+ }
416
+ // ── Self-Evaluation (heuristic, no LLM call) ──
417
+ selfEvaluate(message, response, toolsUsed) {
418
+ let score = 0.5; // neutral baseline
419
+ // Response length appropriateness
420
+ if (message.length > 50 && response.length < 20) {
421
+ score -= 0.15;
422
+ }
423
+ else if (response.length > 50) {
424
+ score += 0.1;
425
+ }
426
+ // Tool success signals
427
+ if (toolsUsed.length > 0) {
428
+ const errorPatterns = ['error', 'failed', 'could not', 'unable to', 'not found', 'permission denied'];
429
+ const responseLower = response.toLowerCase();
430
+ const errorCount = errorPatterns.filter(p => responseLower.includes(p)).length;
431
+ if (errorCount === 0)
432
+ score += 0.2;
433
+ else if (errorCount >= 3)
434
+ score -= 0.2;
435
+ }
436
+ else {
437
+ score += 0.05;
438
+ }
439
+ // Pattern match bonus
440
+ try {
441
+ if (this.pendingRouteAgent)
442
+ score += 0.1;
443
+ }
444
+ catch { /* non-critical */ }
445
+ // Actionable content bonus (code blocks, file paths)
446
+ if (response.includes('```') || response.match(/(?:\/[\w./-]+\.\w+)/)) {
447
+ score += 0.1;
448
+ }
449
+ // Repetition penalty
450
+ const recentHashes = this.state.evalHistory.slice(-3).map(e => e.messageHash);
451
+ if (recentHashes.includes(shortHash(response.slice(0, 200)))) {
452
+ score -= 0.1;
453
+ }
454
+ // Clamp to [0, 1]
455
+ return Math.max(0, Math.min(1, score));
456
+ }
457
+ // ── Policy Synthesis ──
458
+ synthesizePolicy(confidence) {
459
+ // High confidence + high success rate → exploit (use proven approaches)
460
+ if (confidence > 0.7 && this.state.successRate > 0.7)
461
+ return 'exploit';
462
+ // Low confidence or low success → explore (try new approaches)
463
+ if (confidence < 0.4 || this.state.successRate < 0.3)
464
+ return 'explore';
465
+ return 'balanced';
466
+ }
467
+ // ── Graph Memory Logging ──
468
+ async logToolToGraph(toolName, args) {
469
+ try {
470
+ const graph = await getGraphMemory();
471
+ // Add the tool as an entity
472
+ const toolNode = graph.findNode(toolName);
473
+ let toolNodeId;
474
+ if (toolNode.length > 0) {
475
+ toolNodeId = toolNode[0].id;
476
+ }
477
+ else {
478
+ const added = graph.addNode('entity', toolName, { kind: 'tool', lastUsed: new Date().toISOString() });
479
+ toolNodeId = added?.id || '';
480
+ }
481
+ // If we have a session goal, connect tool to goal
482
+ if (toolNodeId && this.state.activeGoals.length > 0) {
483
+ const currentGoal = this.state.activeGoals[this.state.activeGoals.length - 1];
484
+ const goalNodes = graph.findNode(currentGoal.description.slice(0, 50));
485
+ if (goalNodes.length > 0) {
486
+ graph.addEdge(toolNodeId, goalNodes[0].id, 'used_for', 0.5);
487
+ }
488
+ }
489
+ graph.save();
490
+ }
491
+ catch { /* graph logging is non-critical */ }
492
+ }
493
+ // ── Insight Management ──
494
+ addInsight(content, source, confidence) {
495
+ this.state.recentInsights.push({
496
+ id: shortId(),
497
+ content,
498
+ source,
499
+ confidence,
500
+ timestamp: new Date().toISOString(),
501
+ });
502
+ // Trim to max
503
+ if (this.state.recentInsights.length > MAX_INSIGHTS) {
504
+ this.state.recentInsights = this.state.recentInsights.slice(-MAX_INSIGHTS);
505
+ }
506
+ }
507
+ // ── Goal Management ──
508
+ addGoal(description, priority = 0.5) {
509
+ const goal = {
510
+ id: shortId(),
511
+ description,
512
+ priority: Math.max(0, Math.min(1, priority)),
513
+ status: 'active',
514
+ created: new Date().toISOString(),
515
+ toolsUsed: [],
516
+ };
517
+ this.state.activeGoals.push(goal);
518
+ // Trim old completed/abandoned goals
519
+ if (this.state.activeGoals.length > MAX_GOALS) {
520
+ this.state.activeGoals = this.state.activeGoals
521
+ .filter(g => g.status === 'active')
522
+ .slice(-MAX_GOALS);
523
+ }
524
+ this.save();
525
+ return goal;
526
+ }
527
+ completeGoal(goalId) {
528
+ const goal = this.state.activeGoals.find(g => g.id === goalId);
529
+ if (goal) {
530
+ goal.status = 'completed';
531
+ this.save();
532
+ }
533
+ }
534
+ // ── Conflict Detection ──
535
+ recordConflict(modules, description, resolution) {
536
+ this.state.conflictLog.push({
537
+ modules,
538
+ description,
539
+ resolution: resolution ?? null,
540
+ timestamp: new Date().toISOString(),
541
+ });
542
+ if (this.state.conflictLog.length > MAX_CONFLICTS) {
543
+ this.state.conflictLog = this.state.conflictLog.slice(-MAX_CONFLICTS);
544
+ }
545
+ this.save();
546
+ }
547
+ // ── Persistence ──
548
+ load() {
549
+ this.state = loadState();
550
+ if (this.state.patternsLearnedDate !== todayStr()) {
551
+ this.state.patternsLearnedToday = 0;
552
+ this.state.patternsLearnedDate = todayStr();
553
+ }
554
+ }
555
+ save() {
556
+ saveState(this.state);
557
+ }
558
+ // ── Diagnostics ──
559
+ getStats() {
560
+ return {
561
+ totalInteractions: this.state.totalInteractions,
562
+ successRate: Math.round(this.state.successRate * 1000) / 1000,
563
+ patternsLearnedToday: this.state.patternsLearnedToday,
564
+ routingAccuracy: Math.round(this.state.routingAccuracy * 1000) / 1000,
565
+ activeGoals: this.state.activeGoals.filter(g => g.status === 'active').length,
566
+ recentInsights: this.state.recentInsights.length,
567
+ conflicts: this.state.conflictLog.length,
568
+ lastConsolidation: this.state.lastConsolidation,
569
+ policy: this.state.lastPolicy,
570
+ confidenceThreshold: this.state.confidenceThreshold,
571
+ uptimeMs: Date.now() - this.initTime,
572
+ };
573
+ }
574
+ getHealthReport() {
575
+ const stats = this.getStats();
576
+ const lines = [
577
+ '=== Intelligence Coordinator Health Report ===',
578
+ '',
579
+ `Total interactions: ${stats.totalInteractions}`,
580
+ `Success rate: ${(stats.successRate * 100).toFixed(1)}%`,
581
+ `Routing accuracy: ${(stats.routingAccuracy * 100).toFixed(1)}%`,
582
+ `Policy: ${stats.policy}`,
583
+ `Confidence threshold: ${stats.confidenceThreshold}`,
584
+ '',
585
+ `Active goals: ${stats.activeGoals}`,
586
+ `Recent insights: ${stats.recentInsights}`,
587
+ `Conflicts: ${stats.conflicts}`,
588
+ `Patterns learned today: ${stats.patternsLearnedToday}`,
589
+ '',
590
+ `Last consolidation: ${stats.lastConsolidation ?? 'never'}`,
591
+ `Uptime: ${Math.round(stats.uptimeMs / 1000)}s`,
592
+ ];
593
+ // Health checks
594
+ const issues = [];
595
+ if (stats.successRate < 0.3)
596
+ issues.push('LOW: Success rate below 30%');
597
+ if (stats.routingAccuracy < 0.3)
598
+ issues.push('LOW: Routing accuracy below 30%');
599
+ if (stats.totalInteractions > 50 && stats.patternsLearnedToday === 0) {
600
+ issues.push('STALE: No patterns learned today despite activity');
601
+ }
602
+ if (!stats.lastConsolidation) {
603
+ issues.push('PENDING: No consolidation has ever run');
604
+ }
605
+ if (issues.length > 0) {
606
+ lines.push('', '--- Issues ---');
607
+ for (const issue of issues)
608
+ lines.push(` ! ${issue}`);
609
+ }
610
+ else {
611
+ lines.push('', 'All systems nominal.');
612
+ }
613
+ return lines.join('\n');
614
+ }
615
+ getState() {
616
+ return this.state;
617
+ }
618
+ /** Adjust the confidence threshold (e.g., user prefers fewer clarification requests) */
619
+ setConfidenceThreshold(threshold) {
620
+ this.state.confidenceThreshold = Math.max(0, Math.min(1, threshold));
621
+ this.save();
622
+ }
623
+ /** Reset all state (for testing or fresh start) */
624
+ reset() {
625
+ this.state = defaultState();
626
+ this.anticipatedTools = [];
627
+ this.pendingRouteAgent = null;
628
+ this.save();
629
+ }
630
+ }
631
+ // ── Singleton ──
632
+ let singleton = null;
633
+ export function getCoordinator() {
634
+ if (!singleton) {
635
+ singleton = new IntelligenceCoordinator();
636
+ }
637
+ return singleton;
638
+ }
639
+ // ── Tool Registration ──
640
+ /** Register coordinator tools with the kbot tool registry */
641
+ export function registerCoordinatorTools() {
642
+ // Lazy import to avoid circular deps at module load time
643
+ import('./tools/index.js').then(({ registerTool }) => {
644
+ registerTool({
645
+ name: 'coordinator_status',
646
+ description: 'Show intelligence coordinator stats: success rate, routing accuracy, policy, interactions, goals, insights',
647
+ parameters: {},
648
+ tier: 'free',
649
+ execute: async () => {
650
+ const c = getCoordinator();
651
+ const stats = c.getStats();
652
+ return JSON.stringify(stats, null, 2);
653
+ },
654
+ });
655
+ registerTool({
656
+ name: 'coordinator_health',
657
+ description: 'Run a health check on all intelligence subsystems and report issues',
658
+ parameters: {},
659
+ tier: 'free',
660
+ execute: async () => {
661
+ const c = getCoordinator();
662
+ return c.getHealthReport();
663
+ },
664
+ });
665
+ registerTool({
666
+ name: 'coordinator_consolidate',
667
+ description: 'Force a cross-session learning consolidation: self-train patterns, prune graph, derive behaviour rules, synthesize insights',
668
+ parameters: {},
669
+ tier: 'free',
670
+ execute: async () => {
671
+ const c = getCoordinator();
672
+ const result = await c.consolidate();
673
+ return JSON.stringify(result, null, 2);
674
+ },
675
+ });
676
+ }).catch(() => {
677
+ // tools/index.js not available — skip registration
678
+ });
679
+ }
680
+ // Auto-register tools when this module is imported
681
+ registerCoordinatorTools();
682
+ //# sourceMappingURL=coordinator.js.map