kernelbot 1.0.26 → 1.0.28

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 (41) hide show
  1. package/README.md +198 -124
  2. package/bin/kernel.js +201 -4
  3. package/package.json +1 -1
  4. package/src/agent.js +397 -222
  5. package/src/automation/automation-manager.js +377 -0
  6. package/src/automation/automation.js +79 -0
  7. package/src/automation/index.js +2 -0
  8. package/src/automation/scheduler.js +141 -0
  9. package/src/bot.js +667 -21
  10. package/src/conversation.js +33 -0
  11. package/src/intents/detector.js +50 -0
  12. package/src/intents/index.js +2 -0
  13. package/src/intents/planner.js +58 -0
  14. package/src/persona.js +68 -0
  15. package/src/prompts/orchestrator.js +76 -0
  16. package/src/prompts/persona.md +21 -0
  17. package/src/prompts/system.js +59 -6
  18. package/src/prompts/workers.js +89 -0
  19. package/src/providers/anthropic.js +23 -16
  20. package/src/providers/base.js +76 -2
  21. package/src/providers/index.js +1 -0
  22. package/src/providers/models.js +2 -1
  23. package/src/providers/openai-compat.js +5 -3
  24. package/src/security/confirm.js +7 -2
  25. package/src/skills/catalog.js +506 -0
  26. package/src/skills/custom.js +128 -0
  27. package/src/swarm/job-manager.js +169 -0
  28. package/src/swarm/job.js +67 -0
  29. package/src/swarm/worker-registry.js +74 -0
  30. package/src/tools/browser.js +458 -335
  31. package/src/tools/categories.js +3 -3
  32. package/src/tools/index.js +3 -0
  33. package/src/tools/orchestrator-tools.js +371 -0
  34. package/src/tools/persona.js +32 -0
  35. package/src/utils/config.js +50 -15
  36. package/src/worker.js +305 -0
  37. package/.agents/skills/interface-design/SKILL.md +0 -391
  38. package/.agents/skills/interface-design/references/critique.md +0 -67
  39. package/.agents/skills/interface-design/references/example.md +0 -86
  40. package/.agents/skills/interface-design/references/principles.md +0 -235
  41. package/.agents/skills/interface-design/references/validation.md +0 -48
package/bin/kernel.js CHANGED
@@ -19,9 +19,18 @@ import {
19
19
  } from '../src/utils/display.js';
20
20
  import { createAuditLogger } from '../src/security/audit.js';
21
21
  import { ConversationManager } from '../src/conversation.js';
22
+ import { UserPersonaManager } from '../src/persona.js';
22
23
  import { Agent } from '../src/agent.js';
24
+ import { JobManager } from '../src/swarm/job-manager.js';
23
25
  import { startBot } from '../src/bot.js';
26
+ import { AutomationManager } from '../src/automation/index.js';
24
27
  import { createProvider, PROVIDERS } from '../src/providers/index.js';
28
+ import {
29
+ loadCustomSkills,
30
+ getCustomSkills,
31
+ addCustomSkill,
32
+ deleteCustomSkill,
33
+ } from '../src/skills/custom.js';
25
34
 
26
35
  function showMenu(config) {
27
36
  const providerDef = PROVIDERS[config.brain.provider];
@@ -37,7 +46,9 @@ function showMenu(config) {
37
46
  console.log(` ${chalk.cyan('3.')} View logs`);
38
47
  console.log(` ${chalk.cyan('4.')} View audit logs`);
39
48
  console.log(` ${chalk.cyan('5.')} Change brain model`);
40
- console.log(` ${chalk.cyan('6.')} Exit`);
49
+ console.log(` ${chalk.cyan('6.')} Manage custom skills`);
50
+ console.log(` ${chalk.cyan('7.')} Manage automations`);
51
+ console.log(` ${chalk.cyan('8.')} Exit`);
41
52
  console.log('');
42
53
  }
43
54
 
@@ -114,8 +125,27 @@ async function startBotFlow(config) {
114
125
 
115
126
  const checks = [];
116
127
 
128
+ // Orchestrator always needs Anthropic API key
129
+ checks.push(
130
+ await showStartupCheck('Orchestrator (Anthropic) API', async () => {
131
+ const orchestratorKey = config.orchestrator.api_key || process.env.ANTHROPIC_API_KEY;
132
+ if (!orchestratorKey) throw new Error('ANTHROPIC_API_KEY is required for the orchestrator');
133
+ const provider = createProvider({
134
+ brain: {
135
+ provider: 'anthropic',
136
+ model: config.orchestrator.model,
137
+ max_tokens: config.orchestrator.max_tokens,
138
+ temperature: config.orchestrator.temperature,
139
+ api_key: orchestratorKey,
140
+ },
141
+ });
142
+ await provider.ping();
143
+ }),
144
+ );
145
+
146
+ // Worker brain check
117
147
  checks.push(
118
- await showStartupCheck(`${providerLabel} API`, async () => {
148
+ await showStartupCheck(`Worker (${providerLabel}) API`, async () => {
119
149
  const provider = createProvider(config);
120
150
  await provider.ping();
121
151
  }),
@@ -137,13 +167,174 @@ async function startBotFlow(config) {
137
167
  }
138
168
 
139
169
  const conversationManager = new ConversationManager(config);
140
- const agent = new Agent({ config, conversationManager });
170
+ const personaManager = new UserPersonaManager();
171
+ const jobManager = new JobManager({
172
+ jobTimeoutSeconds: config.swarm.job_timeout_seconds,
173
+ cleanupIntervalMinutes: config.swarm.cleanup_interval_minutes,
174
+ });
175
+
176
+ const automationManager = new AutomationManager();
177
+
178
+ const agent = new Agent({ config, conversationManager, personaManager, jobManager, automationManager });
179
+
180
+ startBot(config, agent, conversationManager, jobManager, automationManager);
181
+
182
+ // Periodic job cleanup and timeout enforcement
183
+ const cleanupMs = (config.swarm.cleanup_interval_minutes || 30) * 60 * 1000;
184
+ setInterval(() => {
185
+ jobManager.cleanup();
186
+ jobManager.enforceTimeouts();
187
+ }, Math.min(cleanupMs, 60_000)); // enforce timeouts every minute at most
141
188
 
142
- startBot(config, agent, conversationManager);
143
189
  showStartupComplete();
144
190
  return true;
145
191
  }
146
192
 
193
+ async function manageCustomSkills(rl) {
194
+ loadCustomSkills();
195
+
196
+ let managing = true;
197
+ while (managing) {
198
+ const customs = getCustomSkills();
199
+ console.log('');
200
+ console.log(chalk.bold(' Custom Skills\n'));
201
+ console.log(` ${chalk.cyan('1.')} Create new skill`);
202
+ console.log(` ${chalk.cyan('2.')} List skills (${customs.length})`);
203
+ console.log(` ${chalk.cyan('3.')} Delete a skill`);
204
+ console.log(` ${chalk.cyan('4.')} Back`);
205
+ console.log('');
206
+
207
+ const choice = await ask(rl, chalk.cyan(' > '));
208
+ switch (choice.trim()) {
209
+ case '1': {
210
+ const name = await ask(rl, chalk.cyan(' Skill name: '));
211
+ if (!name.trim()) {
212
+ console.log(chalk.dim(' Cancelled.\n'));
213
+ break;
214
+ }
215
+ console.log(chalk.dim(' Enter the system prompt (multi-line). Type END on a blank line to finish:\n'));
216
+ const lines = [];
217
+ while (true) {
218
+ const line = await ask(rl, ' ');
219
+ if (line.trim() === 'END') break;
220
+ lines.push(line);
221
+ }
222
+ const prompt = lines.join('\n').trim();
223
+ if (!prompt) {
224
+ console.log(chalk.dim(' Empty prompt — cancelled.\n'));
225
+ break;
226
+ }
227
+ const skill = addCustomSkill({ name: name.trim(), systemPrompt: prompt });
228
+ console.log(chalk.green(`\n ✅ Created: ${skill.name} (${skill.id})\n`));
229
+ break;
230
+ }
231
+ case '2': {
232
+ const customs = getCustomSkills();
233
+ if (!customs.length) {
234
+ console.log(chalk.dim('\n No custom skills yet.\n'));
235
+ break;
236
+ }
237
+ console.log('');
238
+ for (const s of customs) {
239
+ const preview = s.systemPrompt.slice(0, 60).replace(/\n/g, ' ');
240
+ console.log(` 🛠️ ${chalk.bold(s.name)} (${s.id})`);
241
+ console.log(chalk.dim(` ${preview}${s.systemPrompt.length > 60 ? '...' : ''}`));
242
+ }
243
+ console.log('');
244
+ break;
245
+ }
246
+ case '3': {
247
+ const customs = getCustomSkills();
248
+ if (!customs.length) {
249
+ console.log(chalk.dim('\n No custom skills to delete.\n'));
250
+ break;
251
+ }
252
+ console.log('');
253
+ customs.forEach((s, i) => {
254
+ console.log(` ${chalk.cyan(`${i + 1}.`)} ${s.name} (${s.id})`);
255
+ });
256
+ console.log('');
257
+ const pick = await ask(rl, chalk.cyan(' Delete #: '));
258
+ const idx = parseInt(pick, 10) - 1;
259
+ if (idx >= 0 && idx < customs.length) {
260
+ const deleted = deleteCustomSkill(customs[idx].id);
261
+ if (deleted) console.log(chalk.green(`\n 🗑️ Deleted: ${customs[idx].name}\n`));
262
+ else console.log(chalk.dim(' Not found.\n'));
263
+ } else {
264
+ console.log(chalk.dim(' Cancelled.\n'));
265
+ }
266
+ break;
267
+ }
268
+ case '4':
269
+ managing = false;
270
+ break;
271
+ default:
272
+ console.log(chalk.dim(' Invalid choice.\n'));
273
+ }
274
+ }
275
+ }
276
+
277
+ async function manageAutomations(rl) {
278
+ const manager = new AutomationManager();
279
+
280
+ let managing = true;
281
+ while (managing) {
282
+ const autos = manager.listAll();
283
+ console.log('');
284
+ console.log(chalk.bold(' Automations\n'));
285
+ console.log(` ${chalk.cyan('1.')} List all automations (${autos.length})`);
286
+ console.log(` ${chalk.cyan('2.')} Delete an automation`);
287
+ console.log(` ${chalk.cyan('3.')} Back`);
288
+ console.log('');
289
+
290
+ const choice = await ask(rl, chalk.cyan(' > '));
291
+ switch (choice.trim()) {
292
+ case '1': {
293
+ if (!autos.length) {
294
+ console.log(chalk.dim('\n No automations found.\n'));
295
+ break;
296
+ }
297
+ console.log('');
298
+ for (const a of autos) {
299
+ const status = a.enabled ? chalk.green('enabled') : chalk.yellow('paused');
300
+ const next = a.nextRun ? new Date(a.nextRun).toLocaleString() : 'not scheduled';
301
+ console.log(` ${chalk.bold(a.name)} (${a.id}) — chat ${a.chatId}`);
302
+ console.log(chalk.dim(` Status: ${status} | Runs: ${a.runCount} | Next: ${next}`));
303
+ console.log(chalk.dim(` Task: ${a.description.slice(0, 80)}${a.description.length > 80 ? '...' : ''}`));
304
+ }
305
+ console.log('');
306
+ break;
307
+ }
308
+ case '2': {
309
+ if (!autos.length) {
310
+ console.log(chalk.dim('\n No automations to delete.\n'));
311
+ break;
312
+ }
313
+ console.log('');
314
+ autos.forEach((a, i) => {
315
+ console.log(` ${chalk.cyan(`${i + 1}.`)} ${a.name} (${a.id}) — chat ${a.chatId}`);
316
+ });
317
+ console.log('');
318
+ const pick = await ask(rl, chalk.cyan(' Delete #: '));
319
+ const idx = parseInt(pick, 10) - 1;
320
+ if (idx >= 0 && idx < autos.length) {
321
+ const deleted = manager.delete(autos[idx].id);
322
+ if (deleted) console.log(chalk.green(`\n 🗑️ Deleted: ${autos[idx].name}\n`));
323
+ else console.log(chalk.dim(' Not found.\n'));
324
+ } else {
325
+ console.log(chalk.dim(' Cancelled.\n'));
326
+ }
327
+ break;
328
+ }
329
+ case '3':
330
+ managing = false;
331
+ break;
332
+ default:
333
+ console.log(chalk.dim(' Invalid choice.\n'));
334
+ }
335
+ }
336
+ }
337
+
147
338
  async function main() {
148
339
  showLogo();
149
340
 
@@ -177,6 +368,12 @@ async function main() {
177
368
  await changeBrainModel(config, rl);
178
369
  break;
179
370
  case '6':
371
+ await manageCustomSkills(rl);
372
+ break;
373
+ case '7':
374
+ await manageAutomations(rl);
375
+ break;
376
+ case '8':
180
377
  running = false;
181
378
  break;
182
379
  default:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kernelbot",
3
- "version": "1.0.26",
3
+ "version": "1.0.28",
4
4
  "description": "KernelBot — AI engineering agent with full OS control",
5
5
  "type": "module",
6
6
  "author": "Abdullah Al-Taheri <abdullah@altaheri.me>",