@kernel.chat/kbot 3.66.0 → 3.68.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.
package/dist/cli.js CHANGED
@@ -27,7 +27,7 @@ import { banner, bannerCompact, bannerAuth, prompt as kbotPrompt, printError, pr
27
27
  import { checkForUpdate, selfUpdate } from './updater.js';
28
28
  import { runTutorial } from './tutorial.js';
29
29
  import { syncOnStartup, schedulePush, flushCloudSync, isCloudSyncEnabled, setCloudToken, getCloudToken } from './cloud-sync.js';
30
- import { getBuddy, getBuddyGreeting, formatBuddyStatus } from './buddy.js';
30
+ import { getBuddy, getBuddyGreeting, formatBuddyStatus, getBuddyDreamNarration } from './buddy.js';
31
31
  import chalk from 'chalk';
32
32
  import { createRequire } from 'node:module';
33
33
  const __require = createRequire(import.meta.url);
@@ -1005,6 +1005,113 @@ async function main() {
1005
1005
  process.stderr.write(formatMachineProfile(profile));
1006
1006
  }
1007
1007
  });
1008
+ // ── Watchdog — Service & System Status Dashboard ──
1009
+ program
1010
+ .command('watchdog')
1011
+ .alias('wd')
1012
+ .description('Service watchdog — live status of all kbot background services and system health')
1013
+ .option('--json', 'Output as JSON')
1014
+ .option('--restart <service>', 'Restart a specific service (email-agent, discovery, serve, discord, mlx, collective-sync, daemon, kbot-local)')
1015
+ .action(async (opts) => {
1016
+ const { getSystemHealth, getServiceStatus, restartService } = await import('./tools/watchdog.js');
1017
+ // ── Restart mode ──
1018
+ if (opts.restart) {
1019
+ const result = restartService(opts.restart);
1020
+ if (result.success) {
1021
+ console.log();
1022
+ console.log(` ${chalk.hex('#4ADE80')('✓')} ${result.message}`);
1023
+ console.log();
1024
+ }
1025
+ else {
1026
+ console.log();
1027
+ console.log(` ${chalk.hex('#F87171')('✗')} ${result.message}`);
1028
+ console.log();
1029
+ }
1030
+ return;
1031
+ }
1032
+ const h = getSystemHealth();
1033
+ // ── JSON mode ──
1034
+ if (opts.json) {
1035
+ console.log(JSON.stringify(h, null, 2));
1036
+ return;
1037
+ }
1038
+ // ── Dashboard rendering ──
1039
+ const ACCENT = chalk.hex('#A78BFA');
1040
+ const GREEN = chalk.hex('#4ADE80');
1041
+ const YELLOW = chalk.hex('#FBBF24');
1042
+ const RED = chalk.hex('#F87171');
1043
+ const DIM = chalk.dim;
1044
+ const running = h.services.filter(s => s.status === 'running').length;
1045
+ const total = h.services.length;
1046
+ const allUp = running === total;
1047
+ // Box drawing
1048
+ const W = 42;
1049
+ const box = {
1050
+ tl: '\u256D', tr: '\u256E', bl: '\u2570', br: '\u256F', h: '\u2500', v: '\u2502',
1051
+ pad: (s, w) => {
1052
+ const visible = s.replace(/\x1b\[[0-9;]*m/g, '');
1053
+ const diff = w - visible.length;
1054
+ return diff > 0 ? s + ' '.repeat(diff) : s;
1055
+ },
1056
+ };
1057
+ const row = (content) => {
1058
+ return ACCENT(box.v) + ' ' + box.pad(content, W - 2) + ' ' + ACCENT(box.v);
1059
+ };
1060
+ console.log();
1061
+ console.log(' ' + ACCENT(`${box.tl}${box.h.repeat(W)}${box.tr}`));
1062
+ console.log(' ' + row(`${ACCENT.bold('\u25C6 KBOT SYSTEM STATUS')}`));
1063
+ console.log(' ' + ACCENT(`${box.tl}${box.h.repeat(W)}${box.tr}`.replace(box.tl, '\u251C').replace(box.tr, '\u2524')));
1064
+ // Service count
1065
+ const svcColor = allUp ? GREEN : running > 0 ? YELLOW : RED;
1066
+ console.log(' ' + row(`Services: ${svcColor(`${running}/${total} running`)}`));
1067
+ // CPU
1068
+ console.log(' ' + row(`CPU Load: ${chalk.white(h.loadAvg)}`));
1069
+ // RAM
1070
+ console.log(' ' + row(`RAM: ${chalk.white(h.memUsed)} / ${DIM(h.memTotal)}`));
1071
+ // Disk
1072
+ console.log(' ' + row(`Disk: ${chalk.white(h.diskFree)} free ${DIM(`/ ${h.diskTotal}`)}`));
1073
+ // Ollama
1074
+ const ollamaColor = h.ollamaStatus === 'online' ? GREEN : RED;
1075
+ const ollamaInfo = h.ollamaModels.length > 0 ? ` ${DIM(`(${h.ollamaModels.length} models)`)}` : '';
1076
+ console.log(' ' + row(`Ollama: ${ollamaColor(h.ollamaStatus)}${ollamaInfo}`));
1077
+ // Dreams
1078
+ console.log(' ' + row(`Dreams: ${ACCENT(`${h.dreamCycles}`)} cycles, ${ACCENT(`${h.dreamInsights}`)} insights`));
1079
+ // Memory
1080
+ console.log(' ' + row(`Memory: ${chalk.white(h.kbotMemorySize)}`));
1081
+ console.log(' ' + ACCENT(`${box.bl}${box.h.repeat(W)}${box.br}`));
1082
+ console.log();
1083
+ // ── Services table ──
1084
+ console.log(` ${chalk.bold('SERVICES')}`);
1085
+ console.log(` ${DIM('\u2500'.repeat(64))}`);
1086
+ for (const s of h.services) {
1087
+ const icon = s.status === 'running' ? GREEN('\u2713')
1088
+ : s.status === 'dead' ? RED('\u2717')
1089
+ : DIM('\u2500');
1090
+ const nameStr = chalk.bold(s.shortName.padEnd(18));
1091
+ const pidStr = s.pid ? DIM(`PID ${String(s.pid).padEnd(8)}`) : DIM('PID -'.padEnd(12));
1092
+ let statusStr;
1093
+ if (s.status === 'running') {
1094
+ statusStr = `CPU ${chalk.white(s.cpu.padEnd(7))} MEM ${chalk.white(s.mem.padEnd(7))} up ${GREEN(s.uptime)}`;
1095
+ }
1096
+ else if (s.status === 'dead') {
1097
+ statusStr = RED('dead — restart with: kbot wd --restart ' + s.shortName);
1098
+ }
1099
+ else {
1100
+ statusStr = DIM('not loaded');
1101
+ }
1102
+ console.log(` ${icon} ${nameStr} ${pidStr} ${statusStr}`);
1103
+ }
1104
+ console.log();
1105
+ // Ollama model list
1106
+ if (h.ollamaModels.length > 0) {
1107
+ console.log(` ${chalk.bold('OLLAMA MODELS')}`);
1108
+ console.log(` ${DIM('\u2500'.repeat(64))}`);
1109
+ for (const m of h.ollamaModels) {
1110
+ console.log(` ${ACCENT('\u25B8')} ${chalk.white(m)}`);
1111
+ }
1112
+ console.log();
1113
+ }
1114
+ });
1008
1115
  program
1009
1116
  .command('hardware')
1010
1117
  .description('Detect your hardware tier and get personalized model recommendations for local AI')
@@ -4170,6 +4277,17 @@ async function startRepl(agentOpts, context, tier, byokActive = false, localActi
4170
4277
  : getBuddyGreeting();
4171
4278
  console.log();
4172
4279
  console.log(formatBuddyStatus(greeting));
4280
+ // Dream narration — buddy tells the user what it dreamed about
4281
+ if (!isFirstRun) {
4282
+ try {
4283
+ const dreamNarration = getBuddyDreamNarration();
4284
+ if (dreamNarration) {
4285
+ console.log();
4286
+ console.log(formatBuddyStatus(dreamNarration));
4287
+ }
4288
+ }
4289
+ catch { /* dream narration is non-critical */ }
4290
+ }
4173
4291
  console.log();
4174
4292
  }
4175
4293
  // Seed knowledge on first run — give new users a head start
package/dist/dream.js CHANGED
@@ -22,6 +22,7 @@ import { loadMemory } from './memory.js';
22
22
  import { getTopPatterns, getTopSolutions, getProfileSummary, updateProfile, recordPattern, learnFact, } from './learning.js';
23
23
  import { getMemoryScannerStats } from './memory-scanner.js';
24
24
  import { getLearningReport as getMusicLearningReport, getRecentHistory as getMusicRecentHistory, getPreferences as getMusicPreferences, } from './music-learning.js';
25
+ import { getBehaviorForDream } from './user-behavior.js';
25
26
  import { registerAmendment } from './prompt-evolution.js';
26
27
  // ── Constants ──
27
28
  const DREAM_DIR = join(homedir(), '.kbot', 'memory', 'dreams');
@@ -185,6 +186,8 @@ function buildConsolidationPrompt(sessionHistory, existingInsights, existingMemo
185
186
  : '';
186
187
  musicText = report + (recentText ? `\n\n**Recent production events:**\n${recentText}` : '');
187
188
  }
189
+ // ── Tier 7: User computer behavior — desktop observation ──
190
+ const behaviorText = getBehaviorForDream(48) || '(no behavior data yet)';
188
191
  return `You are a memory consolidation system. Analyze this conversation session and ALL accumulated knowledge tiers to extract durable cross-tier insights.
189
192
 
190
193
  EXISTING DREAM INSIGHTS (Tier 4 — Dream Journal):
@@ -208,6 +211,9 @@ ${scannerText}
208
211
  MUSIC PRODUCTION LEARNING (Tier 6 — Musical Memory):
209
212
  ${musicText}
210
213
 
214
+ USER COMPUTER BEHAVIOR (Tier 7 — Desktop Observation):
215
+ ${behaviorText}
216
+
211
217
  SESSION TO CONSOLIDATE:
212
218
  ${historyText}
213
219
 
@@ -226,6 +232,13 @@ For music/production sessions, pay special attention to:
226
232
  - Cross-domain insights (e.g., coding workflow preferences that mirror production habits)
227
233
  Use category "music" for production-specific insights.
228
234
 
235
+ For desktop behavior data, look for:
236
+ - Workflow patterns (which apps are always open together, e.g., IDE + terminal + browser)
237
+ - Productivity habits (active hours, app switching frequency)
238
+ - Context-switching tendencies (many apps vs focused few)
239
+ - Tool preferences (which creative/dev tools dominate)
240
+ - Cross-domain insights (e.g., "user switches to music production apps in evening hours")
241
+
229
242
  Pay special attention to:
230
243
  - Patterns that confirm or contradict existing insights
231
244
  - Scanner corrections that reveal unrecognized preferences
@@ -0,0 +1,2 @@
1
+ export declare function registerBehaviorTools(): void;
2
+ //# sourceMappingURL=behavior-tools.d.ts.map
@@ -0,0 +1,63 @@
1
+ // kbot Behavior Tools — Agent-accessible desktop behavior observation
2
+ //
3
+ // Exposes the user behavior system to kbot's tool system so agents can:
4
+ // - Capture a behavior snapshot (what apps are open now)
5
+ // - View behavior patterns from recent history
6
+ //
7
+ // macOS only. Privacy-conscious — app names and window titles only.
8
+ // No window contents, no screenshots, no keylogging.
9
+ import { registerTool } from './index.js';
10
+ import { captureUserBehavior, getBehaviorSummary } from '../user-behavior.js';
11
+ export function registerBehaviorTools() {
12
+ // ── behavior_snapshot ──
13
+ // Capture current desktop state
14
+ registerTool({
15
+ name: 'behavior_snapshot',
16
+ description: 'Capture a snapshot of the user\'s current desktop state: which apps are visible, which app is active (frontmost), the active window title, screen count, and whether Ollama is running. macOS only. Privacy-conscious — captures app names and window titles only, never window contents.',
17
+ parameters: {},
18
+ tier: 'free',
19
+ timeout: 10_000, // 10s — osascript can be slow
20
+ execute: async () => {
21
+ const snapshot = captureUserBehavior();
22
+ if (!snapshot) {
23
+ return 'Behavior capture unavailable (macOS only, or osascript failed).';
24
+ }
25
+ const lines = [
26
+ 'Behavior Snapshot',
27
+ `Time: ${snapshot.timestamp} (${snapshot.hour}:00, ${['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][snapshot.dayOfWeek]})`,
28
+ `Active app: ${snapshot.activeApp || '(none)'}`,
29
+ `Active window: ${snapshot.activeWindowTitle || '(none)'}`,
30
+ `Screens: ${snapshot.screenCount}`,
31
+ `Ollama running: ${snapshot.ollamaRunning ? 'yes' : 'no'}`,
32
+ '',
33
+ `Visible apps (${snapshot.visibleApps.length}):`,
34
+ ...snapshot.visibleApps.map(a => ` - ${a}`),
35
+ ];
36
+ return lines.join('\n');
37
+ },
38
+ });
39
+ // ── behavior_summary ──
40
+ // Show behavior patterns from recent history
41
+ registerTool({
42
+ name: 'behavior_summary',
43
+ description: 'Show user behavior patterns from recent history: most-used apps, active hours, app switching patterns, app combinations frequently seen together. Reads from stored snapshots. Use this to understand the user\'s workflow habits and desktop patterns.',
44
+ parameters: {
45
+ hours: {
46
+ type: 'number',
47
+ description: 'How many hours of history to analyze (default: 24, max: 168)',
48
+ required: false,
49
+ default: 24,
50
+ },
51
+ },
52
+ tier: 'free',
53
+ execute: async (args) => {
54
+ const hours = Math.min(args.hours || 24, 168);
55
+ const summary = getBehaviorSummary(hours);
56
+ if (!summary) {
57
+ return 'No behavior data available yet. Snapshots are captured at the start of each kbot session. Use behavior_snapshot to capture one now.';
58
+ }
59
+ return summary.text;
60
+ },
61
+ });
62
+ } // end registerBehaviorTools
63
+ //# sourceMappingURL=behavior-tools.js.map
@@ -1,10 +1,11 @@
1
1
  // kbot Buddy Tools — Interact with your terminal companion
2
2
  //
3
- // Two tools:
4
- // buddy_status — Show buddy name, species, mood, and sprite
5
- // buddy_rename — Give your buddy a custom name (persisted to ~/.kbot/buddy.json)
3
+ // Three tools:
4
+ // buddy_status — Show buddy name, species, mood, and sprite
5
+ // buddy_rename — Give your buddy a custom name (persisted to ~/.kbot/buddy.json)
6
+ // buddy_achievements — Show all achievements with unlock status and progress
6
7
  import { registerTool } from './index.js';
7
- import { getBuddy, getBuddySprite, getBuddyGreeting, formatBuddyStatus, renameBuddy, } from '../buddy.js';
8
+ import { getBuddy, getBuddySprite, getBuddyGreeting, getBuddyLevel, formatBuddyStatus, renameBuddy, getAchievements, getAchievementProgress, } from '../buddy.js';
8
9
  const VALID_MOODS = ['idle', 'thinking', 'success', 'error', 'learning'];
9
10
  export function registerBuddyTools() {
10
11
  registerTool({
@@ -19,16 +20,22 @@ export function registerBuddyTools() {
19
20
  tier: 'free',
20
21
  async execute(args) {
21
22
  const buddy = getBuddy();
23
+ const lvl = getBuddyLevel();
22
24
  const mood = args.mood ? String(args.mood) : undefined;
23
25
  if (mood && !VALID_MOODS.includes(mood)) {
24
26
  return `Unknown mood "${mood}". Valid moods: ${VALID_MOODS.join(', ')}`;
25
27
  }
26
28
  const sprite = getBuddySprite(mood).join('\n');
27
29
  const greeting = getBuddyGreeting();
30
+ const xpProgress = lvl.xpToNext !== null
31
+ ? `${lvl.xp}/${lvl.xp + lvl.xpToNext} XP (${lvl.xpToNext} to next)`
32
+ : `${lvl.xp} XP (MAX)`;
28
33
  return [
29
34
  `Name: ${buddy.name}`,
30
35
  `Species: ${buddy.species}`,
31
36
  `Mood: ${mood || buddy.mood}`,
37
+ `Level: ${lvl.level} — ${lvl.title}`,
38
+ `XP: ${xpProgress}`,
32
39
  '',
33
40
  sprite,
34
41
  '',
@@ -59,5 +66,41 @@ export function registerBuddyTools() {
59
66
  return formatBuddyStatus(`${oldName} is now ${newName}!`);
60
67
  },
61
68
  });
69
+ registerTool({
70
+ name: 'buddy_achievements',
71
+ description: 'Show all buddy achievements — unlocked ones with date, locked ones with progress hints. Milestones that unlock as you use kbot.',
72
+ parameters: {},
73
+ tier: 'free',
74
+ async execute() {
75
+ const achievements = getAchievements();
76
+ const buddy = getBuddy();
77
+ const unlocked = achievements.filter(a => a.unlockedAt !== null);
78
+ const locked = achievements.filter(a => a.unlockedAt === null);
79
+ const lines = [];
80
+ lines.push(`=== ${buddy.name}'s Achievements ===`);
81
+ lines.push(`${unlocked.length}/${achievements.length} unlocked`);
82
+ lines.push('');
83
+ if (unlocked.length > 0) {
84
+ lines.push('-- Unlocked --');
85
+ for (const a of unlocked) {
86
+ const date = new Date(a.unlockedAt).toLocaleDateString();
87
+ lines.push(` [${a.icon}] ${a.name} — ${a.description}`);
88
+ lines.push(` Unlocked ${date}`);
89
+ }
90
+ lines.push('');
91
+ }
92
+ if (locked.length > 0) {
93
+ lines.push('-- Locked --');
94
+ for (const a of locked) {
95
+ const progress = getAchievementProgress(a.id);
96
+ lines.push(` [ ] ${a.name} — ${a.description}`);
97
+ if (progress) {
98
+ lines.push(` Progress: ${progress}`);
99
+ }
100
+ }
101
+ }
102
+ return lines.join('\n');
103
+ },
104
+ });
62
105
  }
63
106
  //# sourceMappingURL=buddy-tools.js.map
@@ -303,6 +303,8 @@ const LAZY_MODULE_IMPORTS = [
303
303
  { path: './memory-scanner-tools.js', registerFn: 'registerMemoryScannerTools' },
304
304
  { path: './buddy-tools.js', registerFn: 'registerBuddyTools' },
305
305
  { path: './voice-input-tools.js', registerFn: 'registerVoiceInputTools' },
306
+ { path: './watchdog.js', registerFn: 'registerWatchdogTools' },
307
+ { path: './behavior-tools.js', registerFn: 'registerBehaviorTools' },
306
308
  ];
307
309
  /** Track whether lazy tools have been registered */
308
310
  let lazyToolsRegistered = false;
@@ -0,0 +1,32 @@
1
+ export interface ServiceInfo {
2
+ label: string;
3
+ shortName: string;
4
+ pid: number | null;
5
+ status: 'running' | 'dead' | 'not-loaded';
6
+ cpu: string;
7
+ mem: string;
8
+ uptime: string;
9
+ }
10
+ export interface SystemHealth {
11
+ loadAvg: string;
12
+ memFree: string;
13
+ memTotal: string;
14
+ memUsed: string;
15
+ diskFree: string;
16
+ diskUsed: string;
17
+ diskTotal: string;
18
+ ollamaStatus: string;
19
+ ollamaModels: string[];
20
+ kbotMemorySize: string;
21
+ dreamCycles: number;
22
+ dreamInsights: number;
23
+ services: ServiceInfo[];
24
+ }
25
+ export declare function getServiceStatus(): ServiceInfo[];
26
+ export declare function restartService(name: string): {
27
+ success: boolean;
28
+ message: string;
29
+ };
30
+ export declare function getSystemHealth(): SystemHealth;
31
+ export declare function registerWatchdogTools(): void;
32
+ //# sourceMappingURL=watchdog.d.ts.map