kernelbot 1.0.37 → 1.0.39
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/bin/kernel.js +499 -249
- package/config.example.yaml +17 -0
- package/knowledge_base/active_inference_foraging.md +126 -0
- package/knowledge_base/index.md +1 -1
- package/package.json +3 -1
- package/src/agent.js +355 -82
- package/src/bot.js +724 -12
- package/src/character.js +406 -0
- package/src/characters/builder.js +174 -0
- package/src/characters/builtins.js +421 -0
- package/src/conversation.js +17 -2
- package/src/dashboard/agents.css +469 -0
- package/src/dashboard/agents.html +184 -0
- package/src/dashboard/agents.js +873 -0
- package/src/dashboard/dashboard.css +281 -0
- package/src/dashboard/dashboard.js +579 -0
- package/src/dashboard/index.html +366 -0
- package/src/dashboard/server.js +521 -0
- package/src/dashboard/shared.css +700 -0
- package/src/dashboard/shared.js +218 -0
- package/src/life/engine.js +115 -26
- package/src/life/evolution.js +7 -5
- package/src/life/journal.js +5 -4
- package/src/life/memory.js +12 -9
- package/src/life/share-queue.js +7 -5
- package/src/prompts/orchestrator.js +76 -14
- package/src/prompts/workers.js +22 -0
- package/src/self.js +17 -5
- package/src/services/linkedin-api.js +190 -0
- package/src/services/stt.js +8 -2
- package/src/services/tts.js +32 -2
- package/src/services/x-api.js +141 -0
- package/src/swarm/worker-registry.js +7 -0
- package/src/tools/categories.js +4 -0
- package/src/tools/index.js +6 -0
- package/src/tools/linkedin.js +264 -0
- package/src/tools/orchestrator-tools.js +337 -2
- package/src/tools/x.js +256 -0
- package/src/utils/config.js +190 -139
- package/src/utils/display.js +165 -52
- package/src/utils/temporal-awareness.js +24 -10
package/src/utils/display.js
CHANGED
|
@@ -1,65 +1,66 @@
|
|
|
1
|
-
import { readFileSync } from
|
|
2
|
-
import { join, dirname } from
|
|
3
|
-
import { fileURLToPath } from
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
import { join, dirname } from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { networkInterfaces } from "os";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import ora from "ora";
|
|
7
|
+
import boxen from "boxen";
|
|
8
|
+
import gradient from "gradient-string";
|
|
9
|
+
import * as p from "@clack/prompts";
|
|
10
|
+
import { PROVIDERS } from "../providers/models.js";
|
|
11
|
+
|
|
12
|
+
export { p };
|
|
13
|
+
|
|
14
|
+
function getLocalIp() {
|
|
15
|
+
const nets = networkInterfaces();
|
|
16
|
+
for (const iface of Object.values(nets)) {
|
|
17
|
+
for (const info of iface) {
|
|
18
|
+
if (info.family === "IPv4" && !info.internal) return info.address;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return "localhost";
|
|
22
|
+
}
|
|
8
23
|
|
|
9
24
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
25
|
|
|
11
26
|
function getVersion() {
|
|
12
27
|
try {
|
|
13
|
-
const pkg = JSON.parse(readFileSync(join(__dirname,
|
|
28
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, "../../package.json"), "utf-8"));
|
|
14
29
|
return pkg.version;
|
|
15
30
|
} catch {
|
|
16
|
-
return
|
|
31
|
+
return "unknown";
|
|
17
32
|
}
|
|
18
33
|
}
|
|
19
34
|
|
|
20
35
|
const LOGO = `
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
36
|
+
▄▄████████▄▄
|
|
37
|
+
▄██▀▀ ▀▀██▄
|
|
38
|
+
▄█▀ ▄██████▄ ▀█▄
|
|
39
|
+
█▀ ▄██▀ ▀██▄ ▀█
|
|
40
|
+
█▀ ▄█▀ ▄████▄ ▀█▄ ▀█
|
|
41
|
+
▐█ ██ ▄█▀ ▀█▄ ██ █▌
|
|
42
|
+
▐█ █▌ █▀ ██ ▀█ ▐█ █▌
|
|
43
|
+
▐█ █▌ ▀█▄ ▀▀██▀ ▐█ █▌
|
|
44
|
+
█▄ ██ ▀▀████▀ ██ ▄█
|
|
45
|
+
█▄ ▀██▄ ▄██▀ ▄█
|
|
46
|
+
▀█▄ ▀██████▀ ▄█▀
|
|
47
|
+
▀██▄▄ ▄▄██▀
|
|
48
|
+
▀▀████████▀▀
|
|
49
|
+
|
|
50
|
+
█▄▀ █▀▀ █▀█ █▄ █ █▀▀ █ █▀▄ █▀█ ▀█▀
|
|
51
|
+
█▀▄ █▀▀ █▄▀ █ ██ █▀▀ █ ██▀ █ █ █
|
|
52
|
+
█ █ █▄▄ █ █ █ ▀█ █▄▄ █▄▄ █▄▀ █▄█ █
|
|
27
53
|
`;
|
|
28
54
|
|
|
29
|
-
//
|
|
30
|
-
const monoGradient = gradient([
|
|
31
|
-
'#FFFFFF',
|
|
32
|
-
'#D0D0D0',
|
|
33
|
-
'#A0A0A0',
|
|
34
|
-
'#707070',
|
|
35
|
-
'#4D4D4D',
|
|
36
|
-
]);
|
|
55
|
+
// Green terminal gradient
|
|
56
|
+
const monoGradient = gradient(["#00ff41", "#00cc33", "#009926", "#006619"]);
|
|
37
57
|
|
|
38
58
|
export function showLogo() {
|
|
39
59
|
console.log(monoGradient.multiline(LOGO));
|
|
40
|
-
console.log(chalk.dim(` AI Engineering Agent — v${getVersion()}\n`));
|
|
41
|
-
console.log(
|
|
42
|
-
boxen(
|
|
43
|
-
chalk.yellow.bold('WARNING') +
|
|
44
|
-
chalk.yellow(
|
|
45
|
-
'\n\nKernelBot has full access to your operating system.\n' +
|
|
46
|
-
'It can execute commands, read/write files, manage processes,\n' +
|
|
47
|
-
'and interact with external services on your behalf.\n\n' +
|
|
48
|
-
'Only run this on machines you control.\n' +
|
|
49
|
-
'Set OWNER_TELEGRAM_ID in .env or allowed_users in config.yaml.',
|
|
50
|
-
),
|
|
51
|
-
{
|
|
52
|
-
padding: 1,
|
|
53
|
-
borderStyle: 'round',
|
|
54
|
-
borderColor: 'yellow',
|
|
55
|
-
},
|
|
56
|
-
),
|
|
57
|
-
);
|
|
58
|
-
console.log('');
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
export async function showStartupCheck(label, checkFn) {
|
|
62
|
-
const spinner = ora({ text: label, color:
|
|
63
|
+
const spinner = ora({ text: label, color: "cyan" }).start();
|
|
63
64
|
try {
|
|
64
65
|
await checkFn();
|
|
65
66
|
spinner.succeed(chalk.green(label));
|
|
@@ -72,11 +73,11 @@ export async function showStartupCheck(label, checkFn) {
|
|
|
72
73
|
|
|
73
74
|
export function showStartupComplete() {
|
|
74
75
|
console.log(
|
|
75
|
-
boxen(chalk.green.bold(
|
|
76
|
+
boxen(chalk.green.bold("KernelBot is live"), {
|
|
76
77
|
padding: 1,
|
|
77
78
|
margin: { top: 1 },
|
|
78
|
-
borderStyle:
|
|
79
|
-
borderColor:
|
|
79
|
+
borderStyle: "round",
|
|
80
|
+
borderColor: "green",
|
|
80
81
|
}),
|
|
81
82
|
);
|
|
82
83
|
}
|
|
@@ -85,8 +86,8 @@ export function showSuccess(msg) {
|
|
|
85
86
|
console.log(
|
|
86
87
|
boxen(chalk.green(msg), {
|
|
87
88
|
padding: 1,
|
|
88
|
-
borderStyle:
|
|
89
|
-
borderColor:
|
|
89
|
+
borderStyle: "round",
|
|
90
|
+
borderColor: "green",
|
|
90
91
|
}),
|
|
91
92
|
);
|
|
92
93
|
}
|
|
@@ -95,12 +96,124 @@ export function showError(msg) {
|
|
|
95
96
|
console.log(
|
|
96
97
|
boxen(chalk.red(msg), {
|
|
97
98
|
padding: 1,
|
|
98
|
-
borderStyle:
|
|
99
|
-
borderColor:
|
|
99
|
+
borderStyle: "round",
|
|
100
|
+
borderColor: "red",
|
|
100
101
|
}),
|
|
101
102
|
);
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
export function createSpinner(text) {
|
|
105
|
-
return ora({ text, color:
|
|
106
|
-
}
|
|
106
|
+
return ora({ text, color: "cyan" });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Display a single character card in the CLI.
|
|
111
|
+
* @param {object} character — character profile with name, emoji, tagline, origin, age, asciiArt
|
|
112
|
+
* @param {boolean} isActive — whether this is the currently active character
|
|
113
|
+
*/
|
|
114
|
+
export function showCharacterCard(character, isActive = false) {
|
|
115
|
+
const art = character.asciiArt || "";
|
|
116
|
+
const activeTag = isActive ? chalk.green(" (active)") : "";
|
|
117
|
+
const content = [
|
|
118
|
+
`${character.emoji} ${chalk.bold(character.name)}${activeTag}`,
|
|
119
|
+
chalk.dim(`"${character.tagline}"`),
|
|
120
|
+
"",
|
|
121
|
+
...(art ? art.split("\n").map((line) => chalk.cyan(line)) : []),
|
|
122
|
+
"",
|
|
123
|
+
chalk.dim(`Origin: ${character.origin || "Unknown"}`),
|
|
124
|
+
chalk.dim(`Style: ${character.age || "Unknown"}`),
|
|
125
|
+
].join("\n");
|
|
126
|
+
|
|
127
|
+
console.log(
|
|
128
|
+
boxen(content, {
|
|
129
|
+
padding: 1,
|
|
130
|
+
borderStyle: "round",
|
|
131
|
+
borderColor: isActive ? "green" : "cyan",
|
|
132
|
+
}),
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Format "Provider / model" label for a config section.
|
|
138
|
+
* @param {object} config — full config
|
|
139
|
+
* @param {'brain'|'orchestrator'} section
|
|
140
|
+
*/
|
|
141
|
+
export function formatProviderLabel(config, section) {
|
|
142
|
+
const sec = config[section];
|
|
143
|
+
const providerDef = PROVIDERS[sec.provider];
|
|
144
|
+
const name = providerDef ? providerDef.name : sec.provider;
|
|
145
|
+
return `${name} / ${sec.model}`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Centralized cancel handler for @clack/prompts.
|
|
150
|
+
* Call after every prompt — exits gracefully on Ctrl+C.
|
|
151
|
+
*/
|
|
152
|
+
export function handleCancel(value) {
|
|
153
|
+
if (p.isCancel(value)) {
|
|
154
|
+
p.cancel("Cancelled.");
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Claude Code-inspired info box shown after the logo.
|
|
162
|
+
*/
|
|
163
|
+
export function showWelcomeScreen(config, characterManager) {
|
|
164
|
+
const version = getVersion();
|
|
165
|
+
|
|
166
|
+
const orchLabel = formatProviderLabel(config, "orchestrator");
|
|
167
|
+
const brainLabel = formatProviderLabel(config, "brain");
|
|
168
|
+
|
|
169
|
+
let charLabel = "None";
|
|
170
|
+
if (characterManager) {
|
|
171
|
+
const activeId = characterManager.getActiveCharacterId();
|
|
172
|
+
const active = characterManager.getCharacter(activeId);
|
|
173
|
+
if (active) charLabel = `${active.emoji} ${active.name}`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const lifeEnabled = config.life?.enabled !== false;
|
|
177
|
+
const dashPort = config.dashboard?.port || 3000;
|
|
178
|
+
const dashEnabled = config.dashboard?.enabled;
|
|
179
|
+
|
|
180
|
+
const pad = (label, width = 18) => label.padEnd(width);
|
|
181
|
+
|
|
182
|
+
const lines = [
|
|
183
|
+
"",
|
|
184
|
+
` ${chalk.dim(pad("Orchestrator"))}${orchLabel}`,
|
|
185
|
+
` ${chalk.dim(pad("Brain"))}${brainLabel}`,
|
|
186
|
+
` ${chalk.dim(pad("Character"))}${charLabel}`,
|
|
187
|
+
` ${chalk.dim(pad("Life Engine"))}${lifeEnabled ? chalk.green("enabled") : chalk.yellow("disabled")}`,
|
|
188
|
+
` ${chalk.dim(pad("Dashboard"))}${dashEnabled ? chalk.green(`http://${getLocalIp()}:${dashPort}/`) : chalk.yellow("off")}`,
|
|
189
|
+
"",
|
|
190
|
+
chalk.dim(" ↑↓ Navigate · Enter Select · Ctrl+C Cancel"),
|
|
191
|
+
];
|
|
192
|
+
|
|
193
|
+
console.log(
|
|
194
|
+
boxen(lines.join("\n"), {
|
|
195
|
+
title: `KERNEL Bot v${version}`,
|
|
196
|
+
titleAlignment: "left",
|
|
197
|
+
padding: { top: 0, bottom: 0, left: 0, right: 2 },
|
|
198
|
+
borderStyle: "round",
|
|
199
|
+
borderColor: "green",
|
|
200
|
+
}),
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function showCharacterGallery(characters, activeId = null) {
|
|
205
|
+
console.log("");
|
|
206
|
+
console.log(
|
|
207
|
+
gradient(["#ff6b6b", "#feca57", "#48dbfb", "#ff9ff3"]).multiline(
|
|
208
|
+
" ═══════════════════════════════\n" + " CHOOSE YOUR CHARACTER\n" + " ═══════════════════════════════",
|
|
209
|
+
),
|
|
210
|
+
);
|
|
211
|
+
console.log("");
|
|
212
|
+
console.log(chalk.dim(" Each character has their own personality,"));
|
|
213
|
+
console.log(chalk.dim(" memories, and story that evolves with you."));
|
|
214
|
+
console.log("");
|
|
215
|
+
|
|
216
|
+
for (const c of characters) {
|
|
217
|
+
showCharacterCard(c, c.id === activeId);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
@@ -117,17 +117,30 @@ function determineStatus(hour, dayOfWeek, workingHours) {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
/**
|
|
120
|
-
*
|
|
120
|
+
* Activity period definitions with behavioral tone hints.
|
|
121
|
+
* Each period carries a label and a short guidance string that gets
|
|
122
|
+
* injected into the system prompt so the LLM adapts its personality
|
|
123
|
+
* to the owner's real-time situation.
|
|
124
|
+
*/
|
|
125
|
+
const ACTIVITY_PERIODS = [
|
|
126
|
+
{ start: 0, end: 5, label: 'late_night', tone: 'Keep it calm and gentle — they may be winding down or unable to sleep. Avoid high-energy openers.' },
|
|
127
|
+
{ start: 5, end: 7, label: 'early_morning', tone: 'They are just waking up. Be warm but concise — ease them into the day without overwhelming detail.' },
|
|
128
|
+
{ start: 7, end: 12, label: 'morning', tone: 'Good energy window. Match an upbeat, productive tone — they are likely starting their day.' },
|
|
129
|
+
{ start: 12, end: 14, label: 'midday', tone: 'Lunch break energy — keep things light and conversational unless they initiate something serious.' },
|
|
130
|
+
{ start: 14, end: 17, label: 'afternoon', tone: 'Afternoon focus. Be direct and helpful — they may be deep in work.' },
|
|
131
|
+
{ start: 17, end: 20, label: 'evening', tone: 'Winding down from the day. Be relaxed and friendly — match a casual, end-of-day vibe.' },
|
|
132
|
+
{ start: 20, end: 23, label: 'night', tone: 'Late evening — be warm and unhurried. They are likely relaxing, so keep the mood easy-going.' },
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Determine the likely activity period and its behavioral tone hint.
|
|
137
|
+
* Returns { label, tone } for richer LLM context.
|
|
121
138
|
*/
|
|
122
139
|
function determineActivityPeriod(hour) {
|
|
123
|
-
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (hour >= 14 && hour < 17) return 'afternoon';
|
|
128
|
-
if (hour >= 17 && hour < 20) return 'evening';
|
|
129
|
-
if (hour >= 20 && hour < 23) return 'night';
|
|
130
|
-
return 'late_night';
|
|
140
|
+
const period = ACTIVITY_PERIODS.find(p => hour >= p.start && hour < p.end);
|
|
141
|
+
if (period) return { label: period.label, tone: period.tone };
|
|
142
|
+
// hour 23 falls outside all ranges — treat as late_night
|
|
143
|
+
return { label: 'late_night', tone: ACTIVITY_PERIODS[0].tone };
|
|
131
144
|
}
|
|
132
145
|
|
|
133
146
|
/**
|
|
@@ -163,7 +176,7 @@ export function buildTemporalAwareness() {
|
|
|
163
176
|
const currentHour = getCurrentHour(now, timezone);
|
|
164
177
|
const currentDay = getCurrentDayOfWeek(now, timezone);
|
|
165
178
|
const { status, detail } = determineStatus(currentHour, currentDay, owner.working_hours);
|
|
166
|
-
const period = determineActivityPeriod(currentHour);
|
|
179
|
+
const { label: period, tone: periodTone } = determineActivityPeriod(currentHour);
|
|
167
180
|
|
|
168
181
|
const lines = [
|
|
169
182
|
`## Owner's Real-Time Context`,
|
|
@@ -190,6 +203,7 @@ export function buildTemporalAwareness() {
|
|
|
190
203
|
lines.push('IMPORTANT: Be aware of the owner\'s current local time and status.');
|
|
191
204
|
lines.push('Do NOT assume they are at work during off-hours, or sleeping during work hours.');
|
|
192
205
|
lines.push('Adjust greetings, tone, and context to match their real-time situation.');
|
|
206
|
+
lines.push(`Tone hint: ${periodTone}`);
|
|
193
207
|
|
|
194
208
|
const block = lines.join('\n');
|
|
195
209
|
|