kernelbot 1.0.33 → 1.0.35
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/.env.example +11 -0
- package/README.md +76 -341
- package/bin/kernel.js +134 -15
- package/config.example.yaml +2 -1
- package/goals.md +20 -0
- package/knowledge_base/index.md +11 -0
- package/package.json +2 -1
- package/src/agent.js +166 -19
- package/src/automation/automation-manager.js +16 -0
- package/src/automation/automation.js +6 -2
- package/src/bot.js +295 -163
- package/src/conversation.js +70 -3
- package/src/life/engine.js +87 -68
- package/src/life/evolution.js +4 -8
- package/src/life/improvements.js +2 -6
- package/src/life/journal.js +3 -6
- package/src/life/memory.js +3 -10
- package/src/life/share-queue.js +4 -9
- package/src/prompts/orchestrator.js +21 -12
- package/src/prompts/persona.md +27 -0
- package/src/providers/base.js +51 -8
- package/src/providers/google-genai.js +198 -0
- package/src/providers/index.js +6 -1
- package/src/providers/models.js +6 -2
- package/src/providers/openai-compat.js +25 -11
- package/src/security/auth.js +38 -1
- package/src/services/stt.js +10 -1
- package/src/tools/docker.js +37 -15
- package/src/tools/git.js +6 -0
- package/src/tools/github.js +6 -0
- package/src/tools/jira.js +5 -0
- package/src/tools/monitor.js +13 -15
- package/src/tools/network.js +22 -18
- package/src/tools/os.js +37 -2
- package/src/tools/process.js +21 -14
- package/src/utils/config.js +66 -0
- package/src/utils/date.js +19 -0
- package/src/utils/display.js +1 -1
- package/src/utils/ids.js +12 -0
- package/src/utils/shell.js +31 -0
- package/src/utils/temporal-awareness.js +199 -0
- package/src/utils/timeUtils.js +110 -0
- package/src/utils/truncate.js +42 -0
- package/src/worker.js +2 -18
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quiet Hours utility — configurable "Do Not Disturb" schedule.
|
|
3
|
+
*
|
|
4
|
+
* Resolution order (first defined wins):
|
|
5
|
+
* 1. Environment variables QUIET_HOURS_START / QUIET_HOURS_END (HH:mm)
|
|
6
|
+
* 2. YAML config values config.life.quiet_hours.start / .end (integer hour)
|
|
7
|
+
* 3. Built-in defaults 02:00 – 06:00
|
|
8
|
+
*
|
|
9
|
+
* If neither variable is set, quiet hours are disabled (returns false).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Default quiet-hours window (integer hours). */
|
|
13
|
+
const DEFAULT_START = 2;
|
|
14
|
+
const DEFAULT_END = 6;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Resolve the active quiet-hours window into a normalised { startMinutes, endMinutes } pair.
|
|
18
|
+
* Both values are in "minutes since midnight" (0 – 1439).
|
|
19
|
+
*
|
|
20
|
+
* @param {object} [lifeConfig] - Optional `config.life` object from YAML.
|
|
21
|
+
* @returns {{ startMinutes: number, endMinutes: number }}
|
|
22
|
+
*/
|
|
23
|
+
function resolveWindow(lifeConfig) {
|
|
24
|
+
const envStart = process.env.QUIET_HOURS_START;
|
|
25
|
+
const envEnd = process.env.QUIET_HOURS_END;
|
|
26
|
+
|
|
27
|
+
if (envStart && envEnd) {
|
|
28
|
+
const [startH, startM] = envStart.split(':').map(Number);
|
|
29
|
+
const [endH, endM] = envEnd.split(':').map(Number);
|
|
30
|
+
|
|
31
|
+
if (!isNaN(startH) && !isNaN(startM) && !isNaN(endH) && !isNaN(endM)) {
|
|
32
|
+
return { startMinutes: startH * 60 + startM, endMinutes: endH * 60 + endM };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const startHour = lifeConfig?.quiet_hours?.start ?? DEFAULT_START;
|
|
37
|
+
const endHour = lifeConfig?.quiet_hours?.end ?? DEFAULT_END;
|
|
38
|
+
return { startMinutes: startHour * 60, endMinutes: endHour * 60 };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Check whether a given "minutes since midnight" value falls inside a quiet window.
|
|
43
|
+
*
|
|
44
|
+
* @param {number} current - Current minutes since midnight.
|
|
45
|
+
* @param {number} start - Window start (minutes since midnight).
|
|
46
|
+
* @param {number} end - Window end (minutes since midnight).
|
|
47
|
+
* @returns {boolean}
|
|
48
|
+
*/
|
|
49
|
+
function insideWindow(current, start, end) {
|
|
50
|
+
if (start <= end) {
|
|
51
|
+
return current >= start && current < end;
|
|
52
|
+
}
|
|
53
|
+
// Window crosses midnight (e.g. 22:00 – 06:00)
|
|
54
|
+
return current >= start || current < end;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Check whether the current time falls within "quiet hours".
|
|
59
|
+
*
|
|
60
|
+
* @param {object} [lifeConfig] - Optional `config.life` object from YAML.
|
|
61
|
+
* When provided, `lifeConfig.quiet_hours.start` / `.end` (integer hours)
|
|
62
|
+
* act as the second-priority source before the hardcoded defaults.
|
|
63
|
+
* @returns {boolean} `true` when the current time is inside the quiet window.
|
|
64
|
+
*/
|
|
65
|
+
export function isQuietHours(lifeConfig) {
|
|
66
|
+
const { startMinutes, endMinutes } = resolveWindow(lifeConfig);
|
|
67
|
+
const now = new Date();
|
|
68
|
+
const currentMinutes = now.getHours() * 60 + now.getMinutes();
|
|
69
|
+
return insideWindow(currentMinutes, startMinutes, endMinutes);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Return the resolved quiet-hours configuration for logging / status display.
|
|
74
|
+
*
|
|
75
|
+
* @param {object} [lifeConfig] - Optional `config.life` object from YAML.
|
|
76
|
+
* @returns {{ start: string, end: string, active: boolean }}
|
|
77
|
+
* `start` / `end` are formatted as "HH:MM", `active` reflects the current state.
|
|
78
|
+
*/
|
|
79
|
+
export function getQuietHoursConfig(lifeConfig) {
|
|
80
|
+
const { startMinutes, endMinutes } = resolveWindow(lifeConfig);
|
|
81
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
82
|
+
const fmt = (m) => `${pad(Math.floor(m / 60))}:${pad(m % 60)}`;
|
|
83
|
+
return {
|
|
84
|
+
start: fmt(startMinutes),
|
|
85
|
+
end: fmt(endMinutes),
|
|
86
|
+
active: isQuietHours(lifeConfig),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Calculate the number of milliseconds remaining until the current quiet-hours
|
|
92
|
+
* window ends. Returns `0` when quiet hours are not active.
|
|
93
|
+
*
|
|
94
|
+
* Useful for deferring non-essential work until the window closes.
|
|
95
|
+
*
|
|
96
|
+
* @param {object} [lifeConfig] - Optional `config.life` object from YAML.
|
|
97
|
+
* @returns {number} Milliseconds until quiet hours end (0 if not currently quiet).
|
|
98
|
+
*/
|
|
99
|
+
export function msUntilQuietEnd(lifeConfig) {
|
|
100
|
+
if (!isQuietHours(lifeConfig)) return 0;
|
|
101
|
+
|
|
102
|
+
const { endMinutes } = resolveWindow(lifeConfig);
|
|
103
|
+
const now = new Date();
|
|
104
|
+
const currentMinutes = now.getHours() * 60 + now.getMinutes();
|
|
105
|
+
|
|
106
|
+
let diff = endMinutes - currentMinutes;
|
|
107
|
+
if (diff <= 0) diff += 24 * 60; // crosses midnight
|
|
108
|
+
|
|
109
|
+
return diff * 60_000;
|
|
110
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared tool-result truncation logic.
|
|
3
|
+
* Used by both OrchestratorAgent and WorkerAgent to cap tool outputs
|
|
4
|
+
* before feeding them back into the LLM context window.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const MAX_RESULT_LENGTH = 3000;
|
|
8
|
+
|
|
9
|
+
const LARGE_FIELDS = [
|
|
10
|
+
'stdout', 'stderr', 'content', 'diff', 'output',
|
|
11
|
+
'body', 'html', 'text', 'log', 'logs',
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Truncate a serialized tool result to fit within the context budget.
|
|
16
|
+
*
|
|
17
|
+
* Strategy:
|
|
18
|
+
* 1. If JSON.stringify(result) fits, return it as-is.
|
|
19
|
+
* 2. Otherwise, trim known large string fields to 500 chars each and retry.
|
|
20
|
+
* 3. If still too large, hard-slice the serialized string.
|
|
21
|
+
*
|
|
22
|
+
* @param {string} _name - Tool name (reserved for future per-tool limits)
|
|
23
|
+
* @param {any} result - Raw tool result object
|
|
24
|
+
* @returns {string} JSON string, guaranteed ≤ MAX_RESULT_LENGTH (+tail note)
|
|
25
|
+
*/
|
|
26
|
+
export function truncateToolResult(_name, result) {
|
|
27
|
+
let str = JSON.stringify(result);
|
|
28
|
+
if (str.length <= MAX_RESULT_LENGTH) return str;
|
|
29
|
+
|
|
30
|
+
if (result && typeof result === 'object') {
|
|
31
|
+
const truncated = { ...result };
|
|
32
|
+
for (const field of LARGE_FIELDS) {
|
|
33
|
+
if (typeof truncated[field] === 'string' && truncated[field].length > 500) {
|
|
34
|
+
truncated[field] = truncated[field].slice(0, 500) + `\n... [truncated ${truncated[field].length - 500} chars]`;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
str = JSON.stringify(truncated);
|
|
38
|
+
if (str.length <= MAX_RESULT_LENGTH) return str;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return str.slice(0, MAX_RESULT_LENGTH) + `\n... [truncated, total ${str.length} chars]`;
|
|
42
|
+
}
|
package/src/worker.js
CHANGED
|
@@ -5,9 +5,7 @@ import { getMissingCredential } from './utils/config.js';
|
|
|
5
5
|
import { getWorkerPrompt } from './prompts/workers.js';
|
|
6
6
|
import { getUnifiedSkillById } from './skills/custom.js';
|
|
7
7
|
import { getLogger } from './utils/logger.js';
|
|
8
|
-
|
|
9
|
-
const MAX_RESULT_LENGTH = 3000;
|
|
10
|
-
const LARGE_FIELDS = ['stdout', 'stderr', 'content', 'diff', 'output', 'body', 'html', 'text', 'log', 'logs'];
|
|
8
|
+
import { truncateToolResult } from './utils/truncate.js';
|
|
11
9
|
|
|
12
10
|
/**
|
|
13
11
|
* WorkerAgent — runs a scoped agent loop in the background.
|
|
@@ -371,21 +369,7 @@ export class WorkerAgent {
|
|
|
371
369
|
}
|
|
372
370
|
|
|
373
371
|
_truncateResult(name, result) {
|
|
374
|
-
|
|
375
|
-
if (str.length <= MAX_RESULT_LENGTH) return str;
|
|
376
|
-
|
|
377
|
-
if (result && typeof result === 'object') {
|
|
378
|
-
const truncated = { ...result };
|
|
379
|
-
for (const field of LARGE_FIELDS) {
|
|
380
|
-
if (typeof truncated[field] === 'string' && truncated[field].length > 500) {
|
|
381
|
-
truncated[field] = truncated[field].slice(0, 500) + `\n... [truncated ${truncated[field].length - 500} chars]`;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
str = JSON.stringify(truncated);
|
|
385
|
-
if (str.length <= MAX_RESULT_LENGTH) return str;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
return str.slice(0, MAX_RESULT_LENGTH) + `\n... [truncated, total ${str.length} chars]`;
|
|
372
|
+
return truncateToolResult(name, result);
|
|
389
373
|
}
|
|
390
374
|
|
|
391
375
|
_formatToolSummary(name, input) {
|