kernelbot 1.0.25 → 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 -123
  2. package/bin/kernel.js +201 -4
  3. package/package.json +1 -1
  4. package/src/agent.js +447 -174
  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 +908 -69
  10. package/src/conversation.js +69 -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 +74 -35
  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 +101 -0
  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 +53 -16
  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
@@ -4,11 +4,84 @@
4
4
  */
5
5
 
6
6
  export class BaseProvider {
7
- constructor({ model, maxTokens, temperature, apiKey }) {
7
+ constructor({ model, maxTokens, temperature, apiKey, timeout }) {
8
8
  this.model = model;
9
9
  this.maxTokens = maxTokens;
10
10
  this.temperature = temperature;
11
11
  this.apiKey = apiKey;
12
+ this.timeout = timeout || 60_000;
13
+ }
14
+
15
+ /**
16
+ * Wrap an async LLM call with timeout + single retry on transient errors.
17
+ * Composes an internal timeout AbortController with an optional external signal
18
+ * (e.g. worker cancellation). Either aborting will cancel the call.
19
+ *
20
+ * @param {(signal: AbortSignal) => Promise<any>} fn - The API call, receives composed signal
21
+ * @param {AbortSignal} [externalSignal] - Optional external abort signal
22
+ * @returns {Promise<any>}
23
+ */
24
+ async _callWithResilience(fn, externalSignal) {
25
+ for (let attempt = 1; attempt <= 2; attempt++) {
26
+ const ac = new AbortController();
27
+ const timer = setTimeout(
28
+ () => ac.abort(new Error(`LLM call timed out after ${this.timeout / 1000}s`)),
29
+ this.timeout,
30
+ );
31
+
32
+ // If external signal already aborted, bail immediately
33
+ if (externalSignal?.aborted) {
34
+ clearTimeout(timer);
35
+ throw externalSignal.reason || new Error('Aborted');
36
+ }
37
+
38
+ // Forward external abort to our internal controller
39
+ let removeListener;
40
+ if (externalSignal) {
41
+ const onAbort = () => {
42
+ clearTimeout(timer);
43
+ ac.abort(externalSignal.reason || new Error('Cancelled'));
44
+ };
45
+ externalSignal.addEventListener('abort', onAbort, { once: true });
46
+ removeListener = () => externalSignal.removeEventListener('abort', onAbort);
47
+ }
48
+
49
+ try {
50
+ const result = await fn(ac.signal);
51
+ clearTimeout(timer);
52
+ removeListener?.();
53
+ return result;
54
+ } catch (err) {
55
+ clearTimeout(timer);
56
+ removeListener?.();
57
+
58
+ if (attempt < 2 && this._isTransient(err)) {
59
+ await new Promise((r) => setTimeout(r, 1500));
60
+ continue;
61
+ }
62
+ throw err;
63
+ }
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Determine if an error is transient and worth retrying.
69
+ * Covers connection errors, timeouts, 5xx, and 429 rate limits.
70
+ */
71
+ _isTransient(err) {
72
+ const msg = err?.message || '';
73
+ if (
74
+ msg.includes('Connection error') ||
75
+ msg.includes('ECONNRESET') ||
76
+ msg.includes('socket hang up') ||
77
+ msg.includes('ETIMEDOUT') ||
78
+ msg.includes('fetch failed') ||
79
+ msg.includes('timed out')
80
+ ) {
81
+ return true;
82
+ }
83
+ const status = err?.status || err?.statusCode;
84
+ return (status >= 500 && status < 600) || status === 429;
12
85
  }
13
86
 
14
87
  /**
@@ -17,9 +90,10 @@ export class BaseProvider {
17
90
  * @param {string} opts.system - System prompt
18
91
  * @param {Array} opts.messages - Anthropic-format messages
19
92
  * @param {Array} opts.tools - Anthropic-format tool definitions
93
+ * @param {AbortSignal} [opts.signal] - Optional AbortSignal for cancellation
20
94
  * @returns {Promise<{stopReason: 'end_turn'|'tool_use', text: string, toolCalls: Array<{id,name,input}>, rawContent: Array}>}
21
95
  */
22
- async chat({ system, messages, tools }) {
96
+ async chat({ system, messages, tools, signal }) {
23
97
  throw new Error('chat() not implemented');
24
98
  }
25
99
 
@@ -22,6 +22,7 @@ export function createProvider(config) {
22
22
  maxTokens: max_tokens,
23
23
  temperature,
24
24
  apiKey: api_key,
25
+ timeout: config.brain.timeout,
25
26
  };
26
27
 
27
28
  if (provider === 'anthropic') {
@@ -27,8 +27,9 @@ export const PROVIDERS = {
27
27
  envKey: 'GOOGLE_API_KEY',
28
28
  baseUrl: 'https://generativelanguage.googleapis.com/v1beta/openai/',
29
29
  models: [
30
- { id: 'gemini-2.0-flash', label: 'Gemini 2.0 Flash' },
30
+ { id: 'gemini-2.5-flash', label: 'Gemini 2.5 Flash' },
31
31
  { id: 'gemini-2.5-pro', label: 'Gemini 2.5 Pro' },
32
+ { id: 'gemini-2.0-flash', label: 'Gemini 2.0 Flash' },
32
33
  ],
33
34
  },
34
35
  groq: {
@@ -128,7 +128,7 @@ export class OpenAICompatProvider extends BaseProvider {
128
128
 
129
129
  // ── Public API ──
130
130
 
131
- async chat({ system, messages, tools }) {
131
+ async chat({ system, messages, tools, signal }) {
132
132
  const params = {
133
133
  model: this.model,
134
134
  messages: this._convertMessages(system, messages),
@@ -145,8 +145,10 @@ export class OpenAICompatProvider extends BaseProvider {
145
145
  params.tools = convertedTools;
146
146
  }
147
147
 
148
- const response = await this.client.chat.completions.create(params);
149
- return this._normalizeResponse(response);
148
+ return this._callWithResilience(async (timedSignal) => {
149
+ const response = await this.client.chat.completions.create(params, { signal: timedSignal });
150
+ return this._normalizeResponse(response);
151
+ }, signal);
150
152
  }
151
153
 
152
154
  async ping() {
@@ -6,7 +6,7 @@ const DANGEROUS_PATTERNS = [
6
6
  { tool: 'github_create_repo', pattern: null, label: 'create a GitHub repository' },
7
7
  { tool: 'docker_compose', param: 'action', value: 'down', label: 'take down containers' },
8
8
  { tool: 'git_push', param: 'force', value: true, label: 'force push' },
9
- { tool: 'interact_with_page', pattern: null, label: 'interact with a webpage (click, type, execute scripts)' },
9
+ { tool: 'interact_with_page', param: 'actions', check: (actions) => Array.isArray(actions) && actions.some((a) => a.type === 'evaluate'), label: 'execute JavaScript on a webpage' },
10
10
  ];
11
11
 
12
12
  export function requiresConfirmation(toolName, params, config) {
@@ -22,7 +22,12 @@ export function requiresConfirmation(toolName, params, config) {
22
22
  }
23
23
 
24
24
  // Param value match
25
- if (rule.param && params[rule.param] === rule.value) {
25
+ if (rule.param && rule.value !== undefined && params[rule.param] === rule.value) {
26
+ return rule.label;
27
+ }
28
+
29
+ // Custom check function
30
+ if (rule.param && rule.check && rule.check(params[rule.param])) {
26
31
  return rule.label;
27
32
  }
28
33