@p-sw/brainbox 0.1.2-alpha.8 → 0.1.2-alpha.9
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/index.js +283 -95
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,12 +4,17 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { readFileSync as readFileSync2 } from "fs";
|
|
6
6
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7
|
-
import { dirname as
|
|
7
|
+
import { dirname as dirname3, join as join6 } from "path";
|
|
8
8
|
|
|
9
9
|
// src/utils/logger.ts
|
|
10
10
|
import chalk from "chalk";
|
|
11
|
-
import {
|
|
12
|
-
|
|
11
|
+
import {
|
|
12
|
+
existsSync,
|
|
13
|
+
mkdirSync,
|
|
14
|
+
createWriteStream,
|
|
15
|
+
writeFileSync
|
|
16
|
+
} from "fs";
|
|
17
|
+
import { join } from "path";
|
|
13
18
|
var LEVELS = {
|
|
14
19
|
debug: { rank: 0, color: chalk.gray, stderr: false },
|
|
15
20
|
info: { rank: 1, color: chalk.blue, stderr: false },
|
|
@@ -26,29 +31,61 @@ var ICONS = {
|
|
|
26
31
|
error: "✖",
|
|
27
32
|
fatal: "▲"
|
|
28
33
|
};
|
|
34
|
+
function pad2(n) {
|
|
35
|
+
return n < 10 ? `0${n}` : `${n}`;
|
|
36
|
+
}
|
|
37
|
+
function dateKey(d = new Date) {
|
|
38
|
+
return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
class DailyFileSink {
|
|
42
|
+
dir;
|
|
43
|
+
date;
|
|
44
|
+
stream;
|
|
45
|
+
setDir(dir) {
|
|
46
|
+
if (dir === this.dir)
|
|
47
|
+
return;
|
|
48
|
+
this.stream?.end();
|
|
49
|
+
this.stream = undefined;
|
|
50
|
+
this.date = undefined;
|
|
51
|
+
this.dir = dir;
|
|
52
|
+
if (dir && !existsSync(dir))
|
|
53
|
+
mkdirSync(dir, { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
write(data) {
|
|
56
|
+
if (!this.dir)
|
|
57
|
+
return;
|
|
58
|
+
const today = dateKey();
|
|
59
|
+
if (!this.stream || this.date !== today) {
|
|
60
|
+
this.stream?.end();
|
|
61
|
+
this.date = today;
|
|
62
|
+
if (!existsSync(this.dir))
|
|
63
|
+
mkdirSync(this.dir, { recursive: true });
|
|
64
|
+
this.stream = createWriteStream(join(this.dir, `${today}.log`), {
|
|
65
|
+
flags: "a"
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
this.stream.write(data);
|
|
69
|
+
}
|
|
70
|
+
close() {
|
|
71
|
+
this.stream?.end();
|
|
72
|
+
this.stream = undefined;
|
|
73
|
+
this.date = undefined;
|
|
74
|
+
this.dir = undefined;
|
|
75
|
+
}
|
|
76
|
+
get enabled() {
|
|
77
|
+
return this.dir !== undefined;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
29
80
|
var shared = {
|
|
30
81
|
level: "info",
|
|
31
82
|
timestamps: true,
|
|
32
83
|
colors: chalk.level > 0,
|
|
33
|
-
file: undefined,
|
|
34
|
-
fileStream: undefined,
|
|
35
84
|
json: false,
|
|
36
85
|
silent: false
|
|
37
86
|
};
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return;
|
|
41
|
-
shared.fileStream?.end();
|
|
42
|
-
shared.file = path;
|
|
43
|
-
if (path) {
|
|
44
|
-
const dir = dirname(path);
|
|
45
|
-
if (!existsSync(dir))
|
|
46
|
-
mkdirSync(dir, { recursive: true });
|
|
47
|
-
shared.fileStream = createWriteStream(path, { flags: "a" });
|
|
48
|
-
} else {
|
|
49
|
-
shared.fileStream = undefined;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
87
|
+
var mainSink = new DailyFileSink;
|
|
88
|
+
var llmLogDir;
|
|
52
89
|
function applyShared(options) {
|
|
53
90
|
if (options.level !== undefined)
|
|
54
91
|
shared.level = options.level;
|
|
@@ -60,8 +97,8 @@ function applyShared(options) {
|
|
|
60
97
|
shared.silent = options.silent;
|
|
61
98
|
if (options.json !== undefined)
|
|
62
99
|
shared.json = options.json;
|
|
63
|
-
if ("
|
|
64
|
-
|
|
100
|
+
if ("logDir" in options)
|
|
101
|
+
mainSink.setDir(options.logDir);
|
|
65
102
|
}
|
|
66
103
|
|
|
67
104
|
class Logger {
|
|
@@ -75,8 +112,7 @@ class Logger {
|
|
|
75
112
|
}
|
|
76
113
|
formatTimestamp() {
|
|
77
114
|
const now = new Date;
|
|
78
|
-
|
|
79
|
-
return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
|
|
115
|
+
return `${now.getFullYear()}-${pad2(now.getMonth() + 1)}-${pad2(now.getDate())} ${pad2(now.getHours())}:${pad2(now.getMinutes())}:${pad2(now.getSeconds())}`;
|
|
80
116
|
}
|
|
81
117
|
format(level, message) {
|
|
82
118
|
const ts = shared.timestamps ? `[${this.formatTimestamp()}]` : "";
|
|
@@ -114,8 +150,8 @@ class Logger {
|
|
|
114
150
|
out.write(consoleLine + `
|
|
115
151
|
`);
|
|
116
152
|
}
|
|
117
|
-
if (
|
|
118
|
-
|
|
153
|
+
if (mainSink.enabled) {
|
|
154
|
+
mainSink.write(shared.json ? jsonLine : fileLine);
|
|
119
155
|
}
|
|
120
156
|
}
|
|
121
157
|
debug(message) {
|
|
@@ -146,22 +182,48 @@ class Logger {
|
|
|
146
182
|
applyShared(options);
|
|
147
183
|
}
|
|
148
184
|
close() {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
shared.file = undefined;
|
|
185
|
+
mainSink.close();
|
|
186
|
+
llmLogDir = undefined;
|
|
152
187
|
}
|
|
153
188
|
}
|
|
154
189
|
var logger = new Logger;
|
|
190
|
+
function configureLlmLog(logDir) {
|
|
191
|
+
llmLogDir = logDir;
|
|
192
|
+
if (logDir && !existsSync(logDir))
|
|
193
|
+
mkdirSync(logDir, { recursive: true });
|
|
194
|
+
}
|
|
195
|
+
function isLlmLogEnabled() {
|
|
196
|
+
return llmLogDir !== undefined;
|
|
197
|
+
}
|
|
198
|
+
function sanitizeCaller(caller) {
|
|
199
|
+
const s = caller.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
200
|
+
return s.length > 0 ? s : "unknown";
|
|
201
|
+
}
|
|
202
|
+
function dateTimeKey(d = new Date) {
|
|
203
|
+
return `${dateKey(d)}-${pad2(d.getHours())}-${pad2(d.getMinutes())}-${pad2(d.getSeconds())}`;
|
|
204
|
+
}
|
|
205
|
+
function writeLlmExchange(caller, content) {
|
|
206
|
+
if (!llmLogDir)
|
|
207
|
+
return;
|
|
208
|
+
if (!existsSync(llmLogDir))
|
|
209
|
+
mkdirSync(llmLogDir, { recursive: true });
|
|
210
|
+
const base = `${dateTimeKey()}-${sanitizeCaller(caller)}`;
|
|
211
|
+
let path = join(llmLogDir, `${base}.log`);
|
|
212
|
+
for (let n = 2;existsSync(path); n += 1) {
|
|
213
|
+
path = join(llmLogDir, `${base}-${n}.log`);
|
|
214
|
+
}
|
|
215
|
+
writeFileSync(path, content, "utf8");
|
|
216
|
+
}
|
|
155
217
|
|
|
156
218
|
// src/config/loader.ts
|
|
157
|
-
import { dirname as
|
|
219
|
+
import { dirname, join as join2, resolve } from "path";
|
|
158
220
|
import { z, ZodError } from "zod";
|
|
159
221
|
import { homedir } from "os";
|
|
160
222
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
161
|
-
import { mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "fs";
|
|
162
|
-
var brainboxRoot = process.env["BRAINBOX_ROOT_PATH"] ? resolve(process.cwd(), process.env["BRAINBOX_ROOT_PATH"]) :
|
|
223
|
+
import { mkdirSync as mkdirSync2, readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
224
|
+
var brainboxRoot = process.env["BRAINBOX_ROOT_PATH"] ? resolve(process.cwd(), process.env["BRAINBOX_ROOT_PATH"]) : join2(homedir(), ".brainbox");
|
|
163
225
|
function configFile(file, options) {
|
|
164
|
-
const path = () =>
|
|
226
|
+
const path = () => join2(brainboxRoot, file);
|
|
165
227
|
const read = () => {
|
|
166
228
|
const p = path();
|
|
167
229
|
let raw;
|
|
@@ -173,8 +235,8 @@ function configFile(file, options) {
|
|
|
173
235
|
const defaults = defaultsFromSchema(options.schema);
|
|
174
236
|
const templateStr = (options.header ? options.header + `
|
|
175
237
|
` : "") + (defaults === undefined ? "" : stringifyYaml(defaults));
|
|
176
|
-
mkdirSync2(
|
|
177
|
-
|
|
238
|
+
mkdirSync2(dirname(p), { recursive: true });
|
|
239
|
+
writeFileSync2(p, templateStr);
|
|
178
240
|
raw = defaults ?? {};
|
|
179
241
|
}
|
|
180
242
|
try {
|
|
@@ -191,8 +253,8 @@ function configFile(file, options) {
|
|
|
191
253
|
};
|
|
192
254
|
const write = (value) => {
|
|
193
255
|
const p = path();
|
|
194
|
-
mkdirSync2(
|
|
195
|
-
|
|
256
|
+
mkdirSync2(dirname(p), { recursive: true });
|
|
257
|
+
writeFileSync2(p, stringifyYaml(value));
|
|
196
258
|
};
|
|
197
259
|
const update = (fn) => {
|
|
198
260
|
const next = fn(read());
|
|
@@ -313,7 +375,7 @@ function registerCommand(program, config2) {
|
|
|
313
375
|
|
|
314
376
|
// src/brain/manager.ts
|
|
315
377
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
316
|
-
import { join as
|
|
378
|
+
import { join as join3 } from "path";
|
|
317
379
|
var log = logger.child("brain-manager");
|
|
318
380
|
|
|
319
381
|
class BrainDBManager {
|
|
@@ -322,7 +384,7 @@ class BrainDBManager {
|
|
|
322
384
|
this.root = root;
|
|
323
385
|
}
|
|
324
386
|
dbFile() {
|
|
325
|
-
return
|
|
387
|
+
return join3(this.root, "brains.json");
|
|
326
388
|
}
|
|
327
389
|
async readDB() {
|
|
328
390
|
try {
|
|
@@ -488,7 +550,7 @@ class LLMExecutor {
|
|
|
488
550
|
});
|
|
489
551
|
};
|
|
490
552
|
if (conv.provider === id.provider) {
|
|
491
|
-
return build(conv.provider);
|
|
553
|
+
return withLlmLogging(build(conv.provider));
|
|
492
554
|
}
|
|
493
555
|
const modelToProvider = {
|
|
494
556
|
[conv.model]: conv.provider,
|
|
@@ -499,7 +561,7 @@ class LLMExecutor {
|
|
|
499
561
|
[id.provider]: build(id.provider)
|
|
500
562
|
};
|
|
501
563
|
const fallback = instances[conv.provider];
|
|
502
|
-
return new class extends LLMExecutor {
|
|
564
|
+
return withLlmLogging(new class extends LLMExecutor {
|
|
503
565
|
providerName = "dispatch";
|
|
504
566
|
models = {
|
|
505
567
|
conversation: conv.model,
|
|
@@ -513,9 +575,127 @@ class LLMExecutor {
|
|
|
513
575
|
const exec = instances[modelToProvider[model] ?? ""] ?? fallback;
|
|
514
576
|
return exec.chatWithTools(model, options);
|
|
515
577
|
}
|
|
516
|
-
};
|
|
578
|
+
});
|
|
517
579
|
}
|
|
518
580
|
}
|
|
581
|
+
function formatPayload(value) {
|
|
582
|
+
if (typeof value === "string")
|
|
583
|
+
return value;
|
|
584
|
+
try {
|
|
585
|
+
return JSON.stringify(value, null, 2);
|
|
586
|
+
} catch {
|
|
587
|
+
return String(value);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
function resolveCaller(options, fallback) {
|
|
591
|
+
return options.caller ?? options.jsonSchemaName ?? fallback;
|
|
592
|
+
}
|
|
593
|
+
function withLlmLogging(inner) {
|
|
594
|
+
return new class extends LLMExecutor {
|
|
595
|
+
providerName = inner.providerName;
|
|
596
|
+
models = inner.models;
|
|
597
|
+
async call(model, options) {
|
|
598
|
+
const jsonMode = "jsonSchemaName" in options;
|
|
599
|
+
const caller = resolveCaller({
|
|
600
|
+
caller: options.caller,
|
|
601
|
+
jsonSchemaName: jsonMode ? options.jsonSchemaName : undefined
|
|
602
|
+
}, "call");
|
|
603
|
+
const requestBody = [
|
|
604
|
+
`provider: ${inner.providerName}`,
|
|
605
|
+
`model: ${model}`,
|
|
606
|
+
`caller: ${caller}`,
|
|
607
|
+
`reasoningEffort: ${options.reasoningEffort ?? "-"}`,
|
|
608
|
+
`jsonSchemaName: ${jsonMode ? options.jsonSchemaName : "-"}`,
|
|
609
|
+
"",
|
|
610
|
+
"--- instruction ---",
|
|
611
|
+
options.instruction,
|
|
612
|
+
"",
|
|
613
|
+
"--- message ---",
|
|
614
|
+
options.message,
|
|
615
|
+
...jsonMode ? ["", "--- jsonSchema ---", formatPayload(options.jsonSchema)] : []
|
|
616
|
+
].join(`
|
|
617
|
+
`);
|
|
618
|
+
try {
|
|
619
|
+
const result = await inner.call(model, options);
|
|
620
|
+
if (isLlmLogEnabled()) {
|
|
621
|
+
writeLlmExchange(caller, [
|
|
622
|
+
"======== REQUEST ========",
|
|
623
|
+
requestBody,
|
|
624
|
+
"",
|
|
625
|
+
"======== RESPONSE ========",
|
|
626
|
+
formatPayload(result),
|
|
627
|
+
""
|
|
628
|
+
].join(`
|
|
629
|
+
`));
|
|
630
|
+
}
|
|
631
|
+
return result;
|
|
632
|
+
} catch (err) {
|
|
633
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
634
|
+
if (isLlmLogEnabled()) {
|
|
635
|
+
writeLlmExchange(caller, [
|
|
636
|
+
"======== REQUEST ========",
|
|
637
|
+
requestBody,
|
|
638
|
+
"",
|
|
639
|
+
"======== ERROR ========",
|
|
640
|
+
reason,
|
|
641
|
+
""
|
|
642
|
+
].join(`
|
|
643
|
+
`));
|
|
644
|
+
}
|
|
645
|
+
throw err;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
async chatWithTools(model, options) {
|
|
649
|
+
const caller = resolveCaller(options, "chat");
|
|
650
|
+
const requestBody = [
|
|
651
|
+
`provider: ${inner.providerName}`,
|
|
652
|
+
`model: ${model}`,
|
|
653
|
+
`caller: ${caller}`,
|
|
654
|
+
`reasoningEffort: ${options.reasoningEffort ?? "-"}`,
|
|
655
|
+
`parallelToolCalls: ${options.parallelToolCalls ?? false}`,
|
|
656
|
+
"",
|
|
657
|
+
"--- instruction ---",
|
|
658
|
+
options.instruction,
|
|
659
|
+
"",
|
|
660
|
+
"--- messages ---",
|
|
661
|
+
formatPayload(options.messages),
|
|
662
|
+
"",
|
|
663
|
+
"--- tools ---",
|
|
664
|
+
formatPayload(options.tools)
|
|
665
|
+
].join(`
|
|
666
|
+
`);
|
|
667
|
+
try {
|
|
668
|
+
const result = await inner.chatWithTools(model, options);
|
|
669
|
+
if (isLlmLogEnabled()) {
|
|
670
|
+
writeLlmExchange(caller, [
|
|
671
|
+
"======== REQUEST ========",
|
|
672
|
+
requestBody,
|
|
673
|
+
"",
|
|
674
|
+
"======== RESPONSE ========",
|
|
675
|
+
formatPayload(result),
|
|
676
|
+
""
|
|
677
|
+
].join(`
|
|
678
|
+
`));
|
|
679
|
+
}
|
|
680
|
+
return result;
|
|
681
|
+
} catch (err) {
|
|
682
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
683
|
+
if (isLlmLogEnabled()) {
|
|
684
|
+
writeLlmExchange(caller, [
|
|
685
|
+
"======== REQUEST ========",
|
|
686
|
+
requestBody,
|
|
687
|
+
"",
|
|
688
|
+
"======== ERROR ========",
|
|
689
|
+
reason,
|
|
690
|
+
""
|
|
691
|
+
].join(`
|
|
692
|
+
`));
|
|
693
|
+
}
|
|
694
|
+
throw err;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
};
|
|
698
|
+
}
|
|
519
699
|
function listProviderNames() {
|
|
520
700
|
return LLMExecutor.listProviderNames();
|
|
521
701
|
}
|
|
@@ -1638,11 +1818,11 @@ class AnthropicExecutor extends LLMExecutor {
|
|
|
1638
1818
|
|
|
1639
1819
|
// src/provider/providers/bedrock.ts
|
|
1640
1820
|
import { createHmac, createHash } from "node:crypto";
|
|
1641
|
-
var
|
|
1821
|
+
var pad22 = (n) => n.toString().padStart(2, "0");
|
|
1642
1822
|
function amzDate(now) {
|
|
1643
1823
|
return {
|
|
1644
|
-
date: `${now.getUTCFullYear()}${
|
|
1645
|
-
datetime: `${now.getUTCFullYear()}${
|
|
1824
|
+
date: `${now.getUTCFullYear()}${pad22(now.getUTCMonth() + 1)}${pad22(now.getUTCDate())}`,
|
|
1825
|
+
datetime: `${now.getUTCFullYear()}${pad22(now.getUTCMonth() + 1)}${pad22(now.getUTCDate())}T` + `${pad22(now.getUTCHours())}${pad22(now.getUTCMinutes())}${pad22(now.getUTCSeconds())}Z`
|
|
1646
1826
|
};
|
|
1647
1827
|
}
|
|
1648
1828
|
function signRequest(opts) {
|
|
@@ -2332,14 +2512,14 @@ var llm = new Proxy({}, {
|
|
|
2332
2512
|
// src/provider/promptLoader.ts
|
|
2333
2513
|
import { existsSync as existsSync2 } from "fs";
|
|
2334
2514
|
import { readFile as readFile2 } from "fs/promises";
|
|
2335
|
-
import path, { dirname as
|
|
2515
|
+
import path, { dirname as dirname2 } from "path";
|
|
2336
2516
|
import { fileURLToPath } from "url";
|
|
2337
2517
|
var log5 = logger.child("prompt-loader");
|
|
2338
2518
|
function fileName(promptKey) {
|
|
2339
2519
|
return promptKey.toLowerCase() + ".md";
|
|
2340
2520
|
}
|
|
2341
2521
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
2342
|
-
var __dirname2 =
|
|
2522
|
+
var __dirname2 = dirname2(__filename2);
|
|
2343
2523
|
var PROMPTS_DIR = existsSync2(path.resolve(__dirname2, "prompts")) ? path.resolve(__dirname2, "prompts") : path.resolve(__dirname2, "../../prompts");
|
|
2344
2524
|
async function loadPrompt(promptKey) {
|
|
2345
2525
|
const filePath = path.join(PROMPTS_DIR, fileName(promptKey));
|
|
@@ -2464,14 +2644,14 @@ function translateMessageHistory(personaName, entries) {
|
|
|
2464
2644
|
}
|
|
2465
2645
|
|
|
2466
2646
|
// src/brain/schedule.ts
|
|
2467
|
-
function
|
|
2647
|
+
function pad23(n) {
|
|
2468
2648
|
return n < 10 ? `0${n}` : `${n}`;
|
|
2469
2649
|
}
|
|
2470
2650
|
function formatDateKey(d) {
|
|
2471
|
-
return `${d.getFullYear()}-${
|
|
2651
|
+
return `${d.getFullYear()}-${pad23(d.getMonth() + 1)}-${pad23(d.getDate())}`;
|
|
2472
2652
|
}
|
|
2473
2653
|
function formatMonthKey(d) {
|
|
2474
|
-
return `${d.getFullYear()}-${
|
|
2654
|
+
return `${d.getFullYear()}-${pad23(d.getMonth() + 1)}`;
|
|
2475
2655
|
}
|
|
2476
2656
|
|
|
2477
2657
|
// src/brain/memory.ts
|
|
@@ -2569,12 +2749,12 @@ class Brain {
|
|
|
2569
2749
|
log7.debug(`Brain constructed: id=${brainbase.brainId} name=${brainbase.displayName} space=${space.name}`);
|
|
2570
2750
|
}
|
|
2571
2751
|
async createDailySchedule(datetime) {
|
|
2572
|
-
const
|
|
2573
|
-
log7.debug(`createDailySchedule: starting for ${
|
|
2752
|
+
const dateKey2 = formatDateKey(datetime);
|
|
2753
|
+
log7.debug(`createDailySchedule: starting for ${dateKey2}`);
|
|
2574
2754
|
try {
|
|
2575
|
-
const existing = await this.memory.get(`daily-schedule:${
|
|
2755
|
+
const existing = await this.memory.get(`daily-schedule:${dateKey2}`);
|
|
2576
2756
|
if (existing) {
|
|
2577
|
-
log7.debug(`createDailySchedule: cache hit for ${
|
|
2757
|
+
log7.debug(`createDailySchedule: cache hit for ${dateKey2}`);
|
|
2578
2758
|
try {
|
|
2579
2759
|
return JSON.parse(existing.content);
|
|
2580
2760
|
} catch (parseErr) {
|
|
@@ -2602,7 +2782,7 @@ class Brain {
|
|
|
2602
2782
|
}
|
|
2603
2783
|
const instruction = await loadPrompt("DAILY_SCHEDULE");
|
|
2604
2784
|
const promptMessage = [
|
|
2605
|
-
`Target date: ${
|
|
2785
|
+
`Target date: ${dateKey2} (${datetime.toLocaleDateString("en-US", { weekday: "long" })})`,
|
|
2606
2786
|
`Personality: ${this.brainbase.baseSystemPrompt}`,
|
|
2607
2787
|
monthlySummary ? `Monthly summary for this day: ${monthlySummary}` : "(no monthly summary available for this date)",
|
|
2608
2788
|
`Recent schedule (${twoDaysAgoKey}, 2 days ago): ${twoDaysAgoSchedule ? twoDaysAgoSchedule.items.map((s) => `${s.start} ${s.activity}`).join(", ") : "(no schedule on file for 2 days ago)"}`,
|
|
@@ -2613,6 +2793,7 @@ class Brain {
|
|
|
2613
2793
|
`);
|
|
2614
2794
|
log7.debug(`createDailySchedule: calling identity model`);
|
|
2615
2795
|
const schedule = await llm.call(llm.models.identity, {
|
|
2796
|
+
caller: "daily-schedule",
|
|
2616
2797
|
instruction,
|
|
2617
2798
|
message: promptMessage,
|
|
2618
2799
|
jsonSchemaName: "daily-schedule",
|
|
@@ -2620,15 +2801,15 @@ class Brain {
|
|
|
2620
2801
|
});
|
|
2621
2802
|
log7.debug(`createDailySchedule: model returned ${schedule.items.length} slots`);
|
|
2622
2803
|
await this.memory.add({
|
|
2623
|
-
customId: `daily-schedule:${
|
|
2804
|
+
customId: `daily-schedule:${dateKey2}`,
|
|
2624
2805
|
content: JSON.stringify(schedule),
|
|
2625
2806
|
metadata: {
|
|
2626
2807
|
kind: "schedule",
|
|
2627
2808
|
source: "createDailySchedule",
|
|
2628
|
-
date:
|
|
2809
|
+
date: dateKey2
|
|
2629
2810
|
}
|
|
2630
2811
|
});
|
|
2631
|
-
log7.debug(`createDailySchedule: persisted ${
|
|
2812
|
+
log7.debug(`createDailySchedule: persisted ${dateKey2}`);
|
|
2632
2813
|
return schedule;
|
|
2633
2814
|
} catch (error) {
|
|
2634
2815
|
let reason = error instanceof Error ? error.message + `(${error.name})` : String(error);
|
|
@@ -2640,17 +2821,17 @@ class Brain {
|
|
|
2640
2821
|
}
|
|
2641
2822
|
async regenerateSchedules() {
|
|
2642
2823
|
const today = new Date;
|
|
2824
|
+
const nextMonth = new Date(today.getFullYear(), today.getMonth() + 1, today.getDate());
|
|
2825
|
+
await this.createMonthlySchedule(nextMonth);
|
|
2643
2826
|
const tomorrow = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);
|
|
2644
2827
|
await this.createDailySchedule(tomorrow);
|
|
2645
2828
|
await this.createDailySchedule(today);
|
|
2646
|
-
const nextMonth = new Date(today.getFullYear(), today.getMonth() + 1, today.getDate());
|
|
2647
|
-
await this.createMonthlySchedule(nextMonth);
|
|
2648
2829
|
}
|
|
2649
2830
|
async createMonthlySchedule(datetime) {
|
|
2650
2831
|
const year = datetime.getMonth() === 11 ? datetime.getFullYear() + 1 : datetime.getFullYear();
|
|
2651
2832
|
const month = (datetime.getMonth() + 1) % 12;
|
|
2652
2833
|
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
2653
|
-
const monthKey = `${year}-${
|
|
2834
|
+
const monthKey = `${year}-${pad23(month + 1)}`;
|
|
2654
2835
|
log7.debug(`createMonthlySchedule: starting for ${monthKey}`);
|
|
2655
2836
|
try {
|
|
2656
2837
|
const existing = await this.memory.get(`monthly-schedule:${monthKey}`);
|
|
@@ -2663,7 +2844,7 @@ class Brain {
|
|
|
2663
2844
|
}
|
|
2664
2845
|
}
|
|
2665
2846
|
const twoMonthsAgo = new Date(year, month - 2, 1);
|
|
2666
|
-
const twoMonthsAgoKey = `${twoMonthsAgo.getFullYear()}-${
|
|
2847
|
+
const twoMonthsAgoKey = `${twoMonthsAgo.getFullYear()}-${pad23(twoMonthsAgo.getMonth() + 1)}`;
|
|
2667
2848
|
log7.debug(`createMonthlySchedule: gathering context (history, ${twoMonthsAgoKey})`);
|
|
2668
2849
|
const [history, twoMonthsAgoStored] = await Promise.all([
|
|
2669
2850
|
this.getHistoryFacts(),
|
|
@@ -2691,6 +2872,7 @@ class Brain {
|
|
|
2691
2872
|
`);
|
|
2692
2873
|
log7.debug(`createMonthlySchedule: calling identity model`);
|
|
2693
2874
|
const schedule = await llm.call(llm.models.identity, {
|
|
2875
|
+
caller: "monthly-schedule",
|
|
2694
2876
|
instruction,
|
|
2695
2877
|
message: promptMessage,
|
|
2696
2878
|
jsonSchemaName: "monthly-schedule",
|
|
@@ -2721,11 +2903,11 @@ class Brain {
|
|
|
2721
2903
|
}
|
|
2722
2904
|
log7.debug(`sleepMemory: starting, ${history.length} messages`);
|
|
2723
2905
|
try {
|
|
2724
|
-
const
|
|
2906
|
+
const dateKey2 = formatDateKey(datetime);
|
|
2725
2907
|
const instruction = await loadPrompt("MEMOIR");
|
|
2726
2908
|
const historyBlock = translateMessageHistory(this.brainbase.displayName, history);
|
|
2727
2909
|
const promptMessage = [
|
|
2728
|
-
`Date: ${
|
|
2910
|
+
`Date: ${dateKey2}`,
|
|
2729
2911
|
`Personality: ${this.brainbase.baseSystemPrompt}`,
|
|
2730
2912
|
`Conversation log:`,
|
|
2731
2913
|
historyBlock
|
|
@@ -2734,20 +2916,21 @@ class Brain {
|
|
|
2734
2916
|
`);
|
|
2735
2917
|
log7.debug(`sleepMemory: calling identity model`);
|
|
2736
2918
|
const memoir = await llm.call(llm.models.identity, {
|
|
2919
|
+
caller: "sleep-memory",
|
|
2737
2920
|
instruction,
|
|
2738
2921
|
message: promptMessage
|
|
2739
2922
|
});
|
|
2740
2923
|
log7.debug(`sleepMemory: model returned ${memoir.length} chars`);
|
|
2741
2924
|
await this.memory.add({
|
|
2742
|
-
customId: `daily-journal:${
|
|
2925
|
+
customId: `daily-journal:${dateKey2}`,
|
|
2743
2926
|
content: memoir,
|
|
2744
2927
|
metadata: {
|
|
2745
2928
|
kind: "daily-journal",
|
|
2746
2929
|
source: "sleepMemory",
|
|
2747
|
-
date:
|
|
2930
|
+
date: dateKey2
|
|
2748
2931
|
}
|
|
2749
2932
|
});
|
|
2750
|
-
log7.debug(`sleepMemory: journal persisted for ${
|
|
2933
|
+
log7.debug(`sleepMemory: journal persisted for ${dateKey2}`);
|
|
2751
2934
|
return memoir;
|
|
2752
2935
|
} catch (error) {
|
|
2753
2936
|
const reason = error instanceof Error ? error.message : String(error);
|
|
@@ -2756,22 +2939,22 @@ class Brain {
|
|
|
2756
2939
|
}
|
|
2757
2940
|
}
|
|
2758
2941
|
async getTodayScheduledAvailability(datetime) {
|
|
2759
|
-
const
|
|
2942
|
+
const dateKey2 = formatDateKey(datetime);
|
|
2760
2943
|
try {
|
|
2761
|
-
const cached = this.availabilityCache.get(
|
|
2944
|
+
const cached = this.availabilityCache.get(dateKey2);
|
|
2762
2945
|
if (cached) {
|
|
2763
|
-
log7.debug(`getTodayScheduledAvailability: cache hit for ${
|
|
2946
|
+
log7.debug(`getTodayScheduledAvailability: cache hit for ${dateKey2}`);
|
|
2764
2947
|
return cached;
|
|
2765
2948
|
}
|
|
2766
|
-
const stored = await this.memory.get(`daily-schedule:${
|
|
2949
|
+
const stored = await this.memory.get(`daily-schedule:${dateKey2}`);
|
|
2767
2950
|
if (!stored) {
|
|
2768
|
-
log7.debug(`getTodayScheduledAvailability: no schedule for ${
|
|
2951
|
+
log7.debug(`getTodayScheduledAvailability: no schedule for ${dateKey2}`);
|
|
2769
2952
|
return null;
|
|
2770
2953
|
}
|
|
2771
2954
|
const dailySchedule = JSON.parse(stored.content);
|
|
2772
2955
|
const availability = await this.deriveAvailabilityFromSchedule(dailySchedule);
|
|
2773
|
-
this.availabilityCache.set(
|
|
2774
|
-
log7.debug(`getTodayScheduledAvailability: cached ${availability.items.length} windows for ${
|
|
2956
|
+
this.availabilityCache.set(dateKey2, availability);
|
|
2957
|
+
log7.debug(`getTodayScheduledAvailability: cached ${availability.items.length} windows for ${dateKey2}`);
|
|
2775
2958
|
return availability;
|
|
2776
2959
|
} catch (error) {
|
|
2777
2960
|
const reason = error instanceof Error ? error.message : String(error);
|
|
@@ -2782,7 +2965,7 @@ class Brain {
|
|
|
2782
2965
|
async getAvailability(datetime = new Date) {
|
|
2783
2966
|
const h = datetime.getHours();
|
|
2784
2967
|
const m = datetime.getMinutes();
|
|
2785
|
-
const hhmm = `${
|
|
2968
|
+
const hhmm = `${pad23(h)}:${pad23(m)}`;
|
|
2786
2969
|
const current = h * 60 + m;
|
|
2787
2970
|
const toMinutes = (s) => {
|
|
2788
2971
|
const [hh = 0, mm = 0] = s.split(":").map((x) => parseInt(x, 10));
|
|
@@ -2795,10 +2978,10 @@ class Brain {
|
|
|
2795
2978
|
return result;
|
|
2796
2979
|
}
|
|
2797
2980
|
async getCurrentAndAdjacentSlots(now) {
|
|
2798
|
-
const
|
|
2799
|
-
const stored = await this.memory.get(`daily-schedule:${
|
|
2981
|
+
const dateKey2 = formatDateKey(now);
|
|
2982
|
+
const stored = await this.memory.get(`daily-schedule:${dateKey2}`);
|
|
2800
2983
|
if (!stored) {
|
|
2801
|
-
log7.debug(`getCurrentAndAdjacentSlots: no schedule for ${
|
|
2984
|
+
log7.debug(`getCurrentAndAdjacentSlots: no schedule for ${dateKey2}`);
|
|
2802
2985
|
return [];
|
|
2803
2986
|
}
|
|
2804
2987
|
let schedule;
|
|
@@ -2831,6 +3014,7 @@ class Brain {
|
|
|
2831
3014
|
personality: this.brainbase.baseSystemPrompt
|
|
2832
3015
|
});
|
|
2833
3016
|
const result = await llm.call(llm.models.identity, {
|
|
3017
|
+
caller: "availability",
|
|
2834
3018
|
instruction,
|
|
2835
3019
|
message: promptMessage,
|
|
2836
3020
|
jsonSchemaName: "availability",
|
|
@@ -2906,6 +3090,7 @@ class Brain {
|
|
|
2906
3090
|
let choice;
|
|
2907
3091
|
try {
|
|
2908
3092
|
choice = await llm.chatWithTools(llm.models.conversation, {
|
|
3093
|
+
caller: initiate ? "start-conversation" : "send-message",
|
|
2909
3094
|
instruction: `${this.brainbase.baseSystemPrompt}
|
|
2910
3095
|
|
|
2911
3096
|
${instruction}`,
|
|
@@ -2977,11 +3162,11 @@ ${instruction}`,
|
|
|
2977
3162
|
${facts || "(none indexed)"}`;
|
|
2978
3163
|
}
|
|
2979
3164
|
async buildScheduleBlock(now) {
|
|
2980
|
-
const
|
|
3165
|
+
const dateKey2 = formatDateKey(now);
|
|
2981
3166
|
const currentSlots = await this.getCurrentAndAdjacentSlots(now);
|
|
2982
3167
|
const currentBlock = currentSlots.length > 0 ? `Currently (around ${now.toTimeString().slice(0, 5)}):
|
|
2983
3168
|
${currentSlots.map((s) => ` ${s.start}-${s.end} ${s.activity}${s.notes ? ` (${s.notes})` : ""}`).join(`
|
|
2984
|
-
`)}` : `Currently (${
|
|
3169
|
+
`)}` : `Currently (${dateKey2} ${now.toTimeString().slice(0, 5)}): (no matching slot in today's schedule)`;
|
|
2985
3170
|
const days = [
|
|
2986
3171
|
{
|
|
2987
3172
|
label: "Yesterday",
|
|
@@ -3004,9 +3189,9 @@ ${currentBlock}
|
|
|
3004
3189
|
${blocks.join(`
|
|
3005
3190
|
`)}`;
|
|
3006
3191
|
}
|
|
3007
|
-
async getDailyScheduleSummary(
|
|
3192
|
+
async getDailyScheduleSummary(dateKey2) {
|
|
3008
3193
|
try {
|
|
3009
|
-
const stored = await this.memory.get(`daily-schedule:${
|
|
3194
|
+
const stored = await this.memory.get(`daily-schedule:${dateKey2}`);
|
|
3010
3195
|
if (!stored)
|
|
3011
3196
|
return null;
|
|
3012
3197
|
const schedule = JSON.parse(stored.content);
|
|
@@ -3077,6 +3262,7 @@ ${blocks.join(`
|
|
|
3077
3262
|
const personaInitInstruction = await loadPrompt("PERSONA_INIT");
|
|
3078
3263
|
log7.debug(`Brain.create: generating description`);
|
|
3079
3264
|
const description = await llm.call(llm.models.identity, {
|
|
3265
|
+
caller: "persona-init",
|
|
3080
3266
|
instruction: personaInitInstruction,
|
|
3081
3267
|
message: seed
|
|
3082
3268
|
});
|
|
@@ -3084,6 +3270,7 @@ ${blocks.join(`
|
|
|
3084
3270
|
const personaSystemInstruction = await loadPrompt("PERSONA_BASE_SYSTEM_PROMPT");
|
|
3085
3271
|
log7.debug(`Brain.create: generating base system prompt + dials`);
|
|
3086
3272
|
const generated = await llm.call(llm.models.identity, {
|
|
3273
|
+
caller: "base-system-prompt",
|
|
3087
3274
|
instruction: personaSystemInstruction,
|
|
3088
3275
|
message: description,
|
|
3089
3276
|
jsonSchemaName: "base-system-prompt",
|
|
@@ -3268,15 +3455,15 @@ class BaseChannel {
|
|
|
3268
3455
|
constructor(brain) {
|
|
3269
3456
|
this.brain = brain;
|
|
3270
3457
|
this.registerCron(SLEEP_MEMORY_CRON_KEY, SLEEP_MEMORY_CRON_PATTERN, async () => {
|
|
3271
|
-
const
|
|
3458
|
+
const dateKey2 = formatDateKey(new Date);
|
|
3272
3459
|
const availability = await this.brain.getAvailability();
|
|
3273
3460
|
if (availability.status !== "offline") {
|
|
3274
3461
|
logger.debug(`sleepMemory cron: skip — availability=${availability.status}`);
|
|
3275
3462
|
return;
|
|
3276
3463
|
}
|
|
3277
|
-
const existing = await this.brain.memory.get(`daily-journal:${
|
|
3464
|
+
const existing = await this.brain.memory.get(`daily-journal:${dateKey2}`);
|
|
3278
3465
|
if (existing) {
|
|
3279
|
-
logger.debug(`sleepMemory cron: skip — journal for ${
|
|
3466
|
+
logger.debug(`sleepMemory cron: skip — journal for ${dateKey2} exists`);
|
|
3280
3467
|
return;
|
|
3281
3468
|
}
|
|
3282
3469
|
const history = await this.getMessageHistoryBetween(new Date(Date.now() - 24 * 60 * 60 * 1000), new Date);
|
|
@@ -3305,8 +3492,8 @@ class BaseChannel {
|
|
|
3305
3492
|
return;
|
|
3306
3493
|
}
|
|
3307
3494
|
const now = new Date;
|
|
3308
|
-
const
|
|
3309
|
-
const count = this.startConversationCounters.get(
|
|
3495
|
+
const dateKey2 = formatDateKey(now);
|
|
3496
|
+
const count = this.startConversationCounters.get(dateKey2) ?? 0;
|
|
3310
3497
|
const countThreshold = this.brain.brainbase.startConversationCountThreshold;
|
|
3311
3498
|
if (count >= countThreshold)
|
|
3312
3499
|
return;
|
|
@@ -3319,7 +3506,7 @@ class BaseChannel {
|
|
|
3319
3506
|
});
|
|
3320
3507
|
if (replies.length === 0)
|
|
3321
3508
|
return;
|
|
3322
|
-
this.startConversationCounters.set(
|
|
3509
|
+
this.startConversationCounters.set(dateKey2, count + 1);
|
|
3323
3510
|
this.startConversationTimeout = true;
|
|
3324
3511
|
setTimeout(() => {
|
|
3325
3512
|
this.startConversationTimeout = false;
|
|
@@ -3891,8 +4078,8 @@ class TelegramChannel extends BaseChannel {
|
|
|
3891
4078
|
|
|
3892
4079
|
// src/utils/daemonClient.ts
|
|
3893
4080
|
import { connect } from "node:net";
|
|
3894
|
-
import { join as
|
|
3895
|
-
var DAEMON_SOCKET_PATH =
|
|
4081
|
+
import { join as join4 } from "node:path";
|
|
4082
|
+
var DAEMON_SOCKET_PATH = join4(config.brainboxRoot, "daemon.sock");
|
|
3896
4083
|
async function sendToDaemon(payload) {
|
|
3897
4084
|
logger.debug(`sendToDaemon: command="${payload.command}" args=${JSON.stringify(payload.args) ?? "null"}`);
|
|
3898
4085
|
let reply;
|
|
@@ -3954,7 +4141,7 @@ function exchangeOnce(payload) {
|
|
|
3954
4141
|
// src/commands/daemon.ts
|
|
3955
4142
|
import { createServer } from "node:net";
|
|
3956
4143
|
import { chmodSync, unlinkSync } from "node:fs";
|
|
3957
|
-
import { join as
|
|
4144
|
+
import { join as join5 } from "node:path";
|
|
3958
4145
|
|
|
3959
4146
|
// src/commands/daemon/commands.ts
|
|
3960
4147
|
var log8 = logger.child("daemon-cmd");
|
|
@@ -4049,9 +4236,10 @@ async function startChannels() {
|
|
|
4049
4236
|
return started;
|
|
4050
4237
|
}
|
|
4051
4238
|
async function daemon() {
|
|
4052
|
-
const
|
|
4053
|
-
logger.configure({
|
|
4054
|
-
|
|
4239
|
+
const logDir = join5(config.brainboxRoot, "logs");
|
|
4240
|
+
logger.configure({ logDir });
|
|
4241
|
+
configureLlmLog(join5(logDir, "llm"));
|
|
4242
|
+
logger.debug(`daemon: boot (logDir=${logDir}, llmLogDir=${join5(logDir, "llm")})`);
|
|
4055
4243
|
const started = await startChannels();
|
|
4056
4244
|
if (started === 0) {
|
|
4057
4245
|
logger.info("No activated brains with channels. Daemon idling.");
|
|
@@ -5572,12 +5760,12 @@ function register8(program) {
|
|
|
5572
5760
|
|
|
5573
5761
|
// src/index.ts
|
|
5574
5762
|
var __filename3 = fileURLToPath2(import.meta.url);
|
|
5575
|
-
var __dirname3 =
|
|
5763
|
+
var __dirname3 = dirname3(__filename3);
|
|
5576
5764
|
logger.configure({ level: config.debug ? "debug" : "info" });
|
|
5577
5765
|
logger.debug(`brainbox starting (debug=${config.debug}, root=${config.brainboxRoot})`);
|
|
5578
5766
|
function getVersion() {
|
|
5579
5767
|
try {
|
|
5580
|
-
const pkgPath =
|
|
5768
|
+
const pkgPath = join6(__dirname3, "..", "package.json");
|
|
5581
5769
|
const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
5582
5770
|
return pkg.version ?? "0.0.0";
|
|
5583
5771
|
} catch {
|