@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.
Files changed (2) hide show
  1. package/dist/index.js +283 -95
  2. 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 dirname4, join as join5 } from "path";
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 { existsSync, mkdirSync, createWriteStream } from "fs";
12
- import { dirname } from "path";
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
- function openFile(path) {
39
- if (path === shared.file)
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 ("file" in options)
64
- openFile(options.file);
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
- const pad = (n) => n.toString().padStart(2, "0");
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 (shared.fileStream) {
118
- shared.fileStream.write(shared.json ? jsonLine : fileLine);
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
- shared.fileStream?.end();
150
- shared.fileStream = undefined;
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 dirname2, join, resolve } from "path";
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"]) : join(homedir(), ".brainbox");
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 = () => join(brainboxRoot, file);
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(dirname2(p), { recursive: true });
177
- writeFileSync(p, templateStr);
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(dirname2(p), { recursive: true });
195
- writeFileSync(p, stringifyYaml(value));
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 join2 } from "path";
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 join2(this.root, "brains.json");
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 pad2 = (n) => n.toString().padStart(2, "0");
1821
+ var pad22 = (n) => n.toString().padStart(2, "0");
1642
1822
  function amzDate(now) {
1643
1823
  return {
1644
- date: `${now.getUTCFullYear()}${pad2(now.getUTCMonth() + 1)}${pad2(now.getUTCDate())}`,
1645
- datetime: `${now.getUTCFullYear()}${pad2(now.getUTCMonth() + 1)}${pad2(now.getUTCDate())}T` + `${pad2(now.getUTCHours())}${pad2(now.getUTCMinutes())}${pad2(now.getUTCSeconds())}Z`
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 dirname3 } from "path";
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 = dirname3(__filename2);
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 pad22(n) {
2647
+ function pad23(n) {
2468
2648
  return n < 10 ? `0${n}` : `${n}`;
2469
2649
  }
2470
2650
  function formatDateKey(d) {
2471
- return `${d.getFullYear()}-${pad22(d.getMonth() + 1)}-${pad22(d.getDate())}`;
2651
+ return `${d.getFullYear()}-${pad23(d.getMonth() + 1)}-${pad23(d.getDate())}`;
2472
2652
  }
2473
2653
  function formatMonthKey(d) {
2474
- return `${d.getFullYear()}-${pad22(d.getMonth() + 1)}`;
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 dateKey = formatDateKey(datetime);
2573
- log7.debug(`createDailySchedule: starting for ${dateKey}`);
2752
+ const dateKey2 = formatDateKey(datetime);
2753
+ log7.debug(`createDailySchedule: starting for ${dateKey2}`);
2574
2754
  try {
2575
- const existing = await this.memory.get(`daily-schedule:${dateKey}`);
2755
+ const existing = await this.memory.get(`daily-schedule:${dateKey2}`);
2576
2756
  if (existing) {
2577
- log7.debug(`createDailySchedule: cache hit for ${dateKey}`);
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: ${dateKey} (${datetime.toLocaleDateString("en-US", { weekday: "long" })})`,
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:${dateKey}`,
2804
+ customId: `daily-schedule:${dateKey2}`,
2624
2805
  content: JSON.stringify(schedule),
2625
2806
  metadata: {
2626
2807
  kind: "schedule",
2627
2808
  source: "createDailySchedule",
2628
- date: dateKey
2809
+ date: dateKey2
2629
2810
  }
2630
2811
  });
2631
- log7.debug(`createDailySchedule: persisted ${dateKey}`);
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}-${pad22(month + 1)}`;
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()}-${pad22(twoMonthsAgo.getMonth() + 1)}`;
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 dateKey = formatDateKey(datetime);
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: ${dateKey}`,
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:${dateKey}`,
2925
+ customId: `daily-journal:${dateKey2}`,
2743
2926
  content: memoir,
2744
2927
  metadata: {
2745
2928
  kind: "daily-journal",
2746
2929
  source: "sleepMemory",
2747
- date: dateKey
2930
+ date: dateKey2
2748
2931
  }
2749
2932
  });
2750
- log7.debug(`sleepMemory: journal persisted for ${dateKey}`);
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 dateKey = formatDateKey(datetime);
2942
+ const dateKey2 = formatDateKey(datetime);
2760
2943
  try {
2761
- const cached = this.availabilityCache.get(dateKey);
2944
+ const cached = this.availabilityCache.get(dateKey2);
2762
2945
  if (cached) {
2763
- log7.debug(`getTodayScheduledAvailability: cache hit for ${dateKey}`);
2946
+ log7.debug(`getTodayScheduledAvailability: cache hit for ${dateKey2}`);
2764
2947
  return cached;
2765
2948
  }
2766
- const stored = await this.memory.get(`daily-schedule:${dateKey}`);
2949
+ const stored = await this.memory.get(`daily-schedule:${dateKey2}`);
2767
2950
  if (!stored) {
2768
- log7.debug(`getTodayScheduledAvailability: no schedule for ${dateKey}`);
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(dateKey, availability);
2774
- log7.debug(`getTodayScheduledAvailability: cached ${availability.items.length} windows for ${dateKey}`);
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 = `${pad22(h)}:${pad22(m)}`;
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 dateKey = formatDateKey(now);
2799
- const stored = await this.memory.get(`daily-schedule:${dateKey}`);
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 ${dateKey}`);
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 dateKey = formatDateKey(now);
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 (${dateKey} ${now.toTimeString().slice(0, 5)}): (no matching slot in today's schedule)`;
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(dateKey) {
3192
+ async getDailyScheduleSummary(dateKey2) {
3008
3193
  try {
3009
- const stored = await this.memory.get(`daily-schedule:${dateKey}`);
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 dateKey = formatDateKey(new Date);
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:${dateKey}`);
3464
+ const existing = await this.brain.memory.get(`daily-journal:${dateKey2}`);
3278
3465
  if (existing) {
3279
- logger.debug(`sleepMemory cron: skip — journal for ${dateKey} exists`);
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 dateKey = formatDateKey(now);
3309
- const count = this.startConversationCounters.get(dateKey) ?? 0;
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(dateKey, count + 1);
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 join3 } from "node:path";
3895
- var DAEMON_SOCKET_PATH = join3(config.brainboxRoot, "daemon.sock");
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 join4 } from "node:path";
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 logFile = join4(config.brainboxRoot, "brainbox.log");
4053
- logger.configure({ file: logFile });
4054
- logger.debug(`daemon: boot (log=${logFile})`);
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 = dirname4(__filename3);
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 = join5(__dirname3, "..", "package.json");
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 {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@p-sw/brainbox",
3
- "version": "0.1.2-alpha.8",
3
+ "version": "0.1.2-alpha.9",
4
4
  "module": "dist/index.js",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",