@p-sw/brainbox 0.2.2 → 0.2.3-beta.1
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/README.md +3 -0
- package/dist/index.js +148 -109
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -22,6 +22,9 @@ Then she runs on a Discord or Telegram account. She greets you. She replies when
|
|
|
22
22
|
|
|
23
23
|
### Getting started
|
|
24
24
|
|
|
25
|
+
|
|
26
|
+
> **Requires [Bun](https://bun.sh).** Channel message history uses `bun:sqlite`, so run BrainBox with Bun (`bun install`, `bun run …`, or install the CLI under Bun). Node alone is not enough.
|
|
27
|
+
|
|
25
28
|
**1. Install**
|
|
26
29
|
|
|
27
30
|
```
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// @bun
|
|
2
3
|
|
|
3
4
|
// src/index.ts
|
|
4
5
|
import { Command } from "commander";
|
|
5
6
|
import { readFileSync as readFileSync2 } from "fs";
|
|
6
7
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7
|
-
import { dirname as dirname3, join as
|
|
8
|
+
import { dirname as dirname3, join as join7 } from "path";
|
|
8
9
|
|
|
9
10
|
// src/utils/logger.ts
|
|
10
11
|
import chalk from "chalk";
|
|
@@ -24,12 +25,12 @@ var LEVELS = {
|
|
|
24
25
|
fatal: { rank: 5, color: chalk.bgRed.white, stderr: true }
|
|
25
26
|
};
|
|
26
27
|
var ICONS = {
|
|
27
|
-
debug: "
|
|
28
|
-
info: "
|
|
29
|
-
success: "
|
|
30
|
-
warn: "
|
|
31
|
-
error: "
|
|
32
|
-
fatal: "
|
|
28
|
+
debug: "\u25C6",
|
|
29
|
+
info: "\u2139",
|
|
30
|
+
success: "\u2714",
|
|
31
|
+
warn: "\u26A0",
|
|
32
|
+
error: "\u2716",
|
|
33
|
+
fatal: "\u25B2"
|
|
33
34
|
};
|
|
34
35
|
function pad2(n) {
|
|
35
36
|
return n < 10 ? `0${n}` : `${n}`;
|
|
@@ -444,7 +445,7 @@ class BrainDBManager {
|
|
|
444
445
|
async isBrainAvailable(brainId) {
|
|
445
446
|
const item = await this.loadBrain(brainId);
|
|
446
447
|
const ok = item !== undefined && this.isBrainReady(item);
|
|
447
|
-
log.debug(`isBrainAvailable: id=${brainId}
|
|
448
|
+
log.debug(`isBrainAvailable: id=${brainId} \u2192 ${ok}`);
|
|
448
449
|
return ok;
|
|
449
450
|
}
|
|
450
451
|
isBrainReady(item) {
|
|
@@ -474,7 +475,7 @@ class BrainDBManager {
|
|
|
474
475
|
var brainManager = new BrainDBManager;
|
|
475
476
|
|
|
476
477
|
// src/brain/index.ts
|
|
477
|
-
import { randomUUID } from "
|
|
478
|
+
import { randomUUID } from "crypto";
|
|
478
479
|
import Supermemory from "supermemory";
|
|
479
480
|
|
|
480
481
|
// src/provider/llm.ts
|
|
@@ -1915,7 +1916,7 @@ class AnthropicExecutor extends LLMExecutor {
|
|
|
1915
1916
|
}
|
|
1916
1917
|
|
|
1917
1918
|
// src/provider/providers/bedrock.ts
|
|
1918
|
-
import { createHmac, createHash } from "
|
|
1919
|
+
import { createHmac, createHash } from "crypto";
|
|
1919
1920
|
var pad22 = (n) => n.toString().padStart(2, "0");
|
|
1920
1921
|
function amzDate(now) {
|
|
1921
1922
|
return {
|
|
@@ -2627,7 +2628,7 @@ async function loadPrompt(promptKey) {
|
|
|
2627
2628
|
const filePath = path.join(PROMPTS_DIR, fileName(promptKey));
|
|
2628
2629
|
log5.debug(`loadPrompt: ${promptKey} (${filePath})`);
|
|
2629
2630
|
const content = await readFile2(filePath, "utf-8");
|
|
2630
|
-
log5.debug(`loadPrompt: ${promptKey}
|
|
2631
|
+
log5.debug(`loadPrompt: ${promptKey} \u2192 ${content.length} chars`);
|
|
2631
2632
|
return content;
|
|
2632
2633
|
}
|
|
2633
2634
|
|
|
@@ -2733,13 +2734,49 @@ var baseSystemPromptSchema = {
|
|
|
2733
2734
|
import { BadRequestResponseError } from "@openrouter/sdk/models/errors";
|
|
2734
2735
|
|
|
2735
2736
|
// src/brain/messageHistory.ts
|
|
2737
|
+
import { Database } from "bun:sqlite";
|
|
2738
|
+
import { mkdirSync as mkdirSync3 } from "fs";
|
|
2739
|
+
import { join as join4 } from "path";
|
|
2740
|
+
var db = null;
|
|
2741
|
+
function getDb() {
|
|
2742
|
+
if (db)
|
|
2743
|
+
return db;
|
|
2744
|
+
mkdirSync3(brainboxRoot, { recursive: true });
|
|
2745
|
+
db = new Database(join4(brainboxRoot, "message-history.sqlite"));
|
|
2746
|
+
db.run(`
|
|
2747
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
2748
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
2749
|
+
brain_id TEXT NOT NULL,
|
|
2750
|
+
sender TEXT NOT NULL,
|
|
2751
|
+
time INTEGER NOT NULL,
|
|
2752
|
+
content TEXT NOT NULL
|
|
2753
|
+
);
|
|
2754
|
+
CREATE INDEX IF NOT EXISTS idx_messages_brain_time
|
|
2755
|
+
ON messages(brain_id, time);
|
|
2756
|
+
`);
|
|
2757
|
+
return db;
|
|
2758
|
+
}
|
|
2759
|
+
function appendMessageHistory(brainId, entry) {
|
|
2760
|
+
getDb().prepare(`INSERT INTO messages (brain_id, sender, time, content)
|
|
2761
|
+
VALUES (?, ?, ?, ?)`).run(brainId, entry.sender, entry.time.getTime(), entry.content);
|
|
2762
|
+
}
|
|
2763
|
+
function getMessageHistory(brainId, start, end) {
|
|
2764
|
+
const rows = getDb().prepare(`SELECT sender, time, content FROM messages
|
|
2765
|
+
WHERE brain_id = ? AND time >= ? AND time <= ?
|
|
2766
|
+
ORDER BY time ASC, id ASC`).all(brainId, start.getTime(), end.getTime());
|
|
2767
|
+
return rows.map((row) => ({
|
|
2768
|
+
sender: row.sender,
|
|
2769
|
+
time: new Date(row.time),
|
|
2770
|
+
content: row.content
|
|
2771
|
+
}));
|
|
2772
|
+
}
|
|
2736
2773
|
function formatTime(time) {
|
|
2737
2774
|
const pad = (n) => n.toString().padStart(2, "0");
|
|
2738
2775
|
return `${time.getFullYear()}-${pad(time.getMonth() + 1)}-${pad(time.getDate())} ${pad(time.getHours())}:${pad(time.getMinutes())}:${pad(time.getSeconds())}`;
|
|
2739
2776
|
}
|
|
2740
2777
|
function translateMessageHistory(personaName, entries) {
|
|
2741
2778
|
return entries.map((entry) => {
|
|
2742
|
-
const label = entry.sender === "persona" ? personaName : "
|
|
2779
|
+
const label = entry.sender === "persona" ? personaName : "\uC0AC\uC6A9\uC790";
|
|
2743
2780
|
return `${label}@${formatTime(entry.time)}: ${entry.content}`;
|
|
2744
2781
|
}).join(`
|
|
2745
2782
|
`);
|
|
@@ -2762,8 +2799,8 @@ var log6 = logger.child("memory");
|
|
|
2762
2799
|
class Memory {
|
|
2763
2800
|
db;
|
|
2764
2801
|
space;
|
|
2765
|
-
constructor(
|
|
2766
|
-
this.db =
|
|
2802
|
+
constructor(db2, space) {
|
|
2803
|
+
this.db = db2;
|
|
2767
2804
|
this.space = space;
|
|
2768
2805
|
log6.debug(`Memory constructed for space=${space.name}`);
|
|
2769
2806
|
}
|
|
@@ -2843,8 +2880,8 @@ class Brain {
|
|
|
2843
2880
|
brainbase;
|
|
2844
2881
|
availabilityCache = new Map;
|
|
2845
2882
|
memory;
|
|
2846
|
-
constructor(
|
|
2847
|
-
this.db =
|
|
2883
|
+
constructor(db2, space, brainbase, memory) {
|
|
2884
|
+
this.db = db2;
|
|
2848
2885
|
this.space = space;
|
|
2849
2886
|
this.brainbase = brainbase;
|
|
2850
2887
|
this.memory = memory ?? new Memory(this.db, this.space);
|
|
@@ -2926,7 +2963,7 @@ class Brain {
|
|
|
2926
2963
|
const nextMonth = new Date(today.getFullYear(), today.getMonth() + 1, today.getDate());
|
|
2927
2964
|
const monthly = await this.createMonthlySchedule(nextMonth);
|
|
2928
2965
|
if (!monthly) {
|
|
2929
|
-
log7.debug(`regenerateSchedules: skip daily
|
|
2966
|
+
log7.debug(`regenerateSchedules: skip daily \u2014 monthly schedule generation failed`);
|
|
2930
2967
|
return;
|
|
2931
2968
|
}
|
|
2932
2969
|
const tomorrow = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);
|
|
@@ -3112,7 +3149,7 @@ class Brain {
|
|
|
3112
3149
|
return slice;
|
|
3113
3150
|
}
|
|
3114
3151
|
async deriveAvailabilityFromSchedule(schedule) {
|
|
3115
|
-
log7.debug(`deriveAvailabilityFromSchedule: ${schedule.items.length} slots
|
|
3152
|
+
log7.debug(`deriveAvailabilityFromSchedule: ${schedule.items.length} slots \u2192 model`);
|
|
3116
3153
|
try {
|
|
3117
3154
|
const instruction = await loadPrompt("SCHEDULE_AVAILABILITY");
|
|
3118
3155
|
const promptMessage = JSON.stringify({
|
|
@@ -3184,7 +3221,7 @@ class Brain {
|
|
|
3184
3221
|
`Conversation so far:`,
|
|
3185
3222
|
historyBlock.length > 0 ? historyBlock : "(no prior messages)",
|
|
3186
3223
|
`New user message(s) to which you must reply:`,
|
|
3187
|
-
newBlock.length > 0 ? newBlock : "(none
|
|
3224
|
+
newBlock.length > 0 ? newBlock : "(none \u2014 open turn)"
|
|
3188
3225
|
].join(`
|
|
3189
3226
|
|
|
3190
3227
|
`);
|
|
@@ -3382,7 +3419,7 @@ ${blocks.join(`
|
|
|
3382
3419
|
const baseSystemPrompt = `${generated.baseSystemPrompt}
|
|
3383
3420
|
|
|
3384
3421
|
${personaSystemFixed}`;
|
|
3385
|
-
const
|
|
3422
|
+
const db2 = new Supermemory({ apiKey: config.supermemoryApiKey });
|
|
3386
3423
|
const brainId = randomUUID();
|
|
3387
3424
|
const space = {
|
|
3388
3425
|
name: `brain:${brainId}`,
|
|
@@ -3399,7 +3436,7 @@ ${personaSystemFixed}`;
|
|
|
3399
3436
|
startConversationTimeThreshold: generated.startConversationTimeThreshold,
|
|
3400
3437
|
activated: false
|
|
3401
3438
|
};
|
|
3402
|
-
const memory = new Memory(
|
|
3439
|
+
const memory = new Memory(db2, space);
|
|
3403
3440
|
await memory.add({
|
|
3404
3441
|
customId: "persona",
|
|
3405
3442
|
content: description,
|
|
@@ -3408,7 +3445,7 @@ ${personaSystemFixed}`;
|
|
|
3408
3445
|
log7.debug(`Brain.create: persona description stored`);
|
|
3409
3446
|
await brainManager.saveBrain(brainId, brainbase);
|
|
3410
3447
|
log7.debug(`Brain.create: brainbase saved (id=${brainId})`);
|
|
3411
|
-
return { brainId, brain: new Brain(
|
|
3448
|
+
return { brainId, brain: new Brain(db2, space, brainbase, memory) };
|
|
3412
3449
|
} catch (error) {
|
|
3413
3450
|
let reason = error instanceof Error ? `${error.message} (${error.name})` : String(error);
|
|
3414
3451
|
if (error instanceof BadRequestResponseError) {
|
|
@@ -3425,8 +3462,8 @@ ${personaSystemFixed}`;
|
|
|
3425
3462
|
log7.debug(`Brain.delete: no brainbase found`);
|
|
3426
3463
|
return false;
|
|
3427
3464
|
}
|
|
3428
|
-
const
|
|
3429
|
-
const memory = new Memory(
|
|
3465
|
+
const db2 = new Supermemory({ apiKey: config.supermemoryApiKey });
|
|
3466
|
+
const memory = new Memory(db2, { name: brainbase.spaceName });
|
|
3430
3467
|
try {
|
|
3431
3468
|
await memory.clear();
|
|
3432
3469
|
} catch (error) {
|
|
@@ -3444,10 +3481,10 @@ ${personaSystemFixed}`;
|
|
|
3444
3481
|
log7.debug(`Brain.load: not loadable (missing or not ready)`);
|
|
3445
3482
|
return null;
|
|
3446
3483
|
}
|
|
3447
|
-
const
|
|
3484
|
+
const db2 = new Supermemory({ apiKey: config.supermemoryApiKey });
|
|
3448
3485
|
const space = { name: brainbase.spaceName };
|
|
3449
3486
|
log7.debug(`Brain.load: ready (channel=${brainbase.channel})`);
|
|
3450
|
-
return new Brain(
|
|
3487
|
+
return new Brain(db2, space, brainbase);
|
|
3451
3488
|
}
|
|
3452
3489
|
}
|
|
3453
3490
|
function formatDatetime(now) {
|
|
@@ -3545,7 +3582,9 @@ var VIEW_THINGS = [
|
|
|
3545
3582
|
"monthly-schedule",
|
|
3546
3583
|
"sending-queue",
|
|
3547
3584
|
"deferred-queue",
|
|
3548
|
-
"today-availability"
|
|
3585
|
+
"today-availability",
|
|
3586
|
+
"persona",
|
|
3587
|
+
"base-system-prompt"
|
|
3549
3588
|
];
|
|
3550
3589
|
|
|
3551
3590
|
class BaseChannel {
|
|
@@ -3576,16 +3615,16 @@ class BaseChannel {
|
|
|
3576
3615
|
if (!force) {
|
|
3577
3616
|
const availability = await this.brain.getAvailability();
|
|
3578
3617
|
if (availability.status !== "offline") {
|
|
3579
|
-
logger.debug(`sleepMemory cron: skip
|
|
3618
|
+
logger.debug(`sleepMemory cron: skip \u2014 availability=${availability.status}`);
|
|
3580
3619
|
return;
|
|
3581
3620
|
}
|
|
3582
3621
|
const existing = await this.brain.memory.get(`daily-journal:${dateKey2}`);
|
|
3583
3622
|
if (existing) {
|
|
3584
|
-
logger.debug(`sleepMemory cron: skip
|
|
3623
|
+
logger.debug(`sleepMemory cron: skip \u2014 journal for ${dateKey2} exists`);
|
|
3585
3624
|
return;
|
|
3586
3625
|
}
|
|
3587
3626
|
}
|
|
3588
|
-
const history =
|
|
3627
|
+
const history = this.getMessageHistory(new Date(Date.now() - 24 * 60 * 60 * 1000), new Date);
|
|
3589
3628
|
await this.brain.sleepMemory(new Date, history);
|
|
3590
3629
|
}
|
|
3591
3630
|
async regenerateSchedules() {
|
|
@@ -3595,11 +3634,11 @@ class BaseChannel {
|
|
|
3595
3634
|
}
|
|
3596
3635
|
async runStartConversation() {
|
|
3597
3636
|
if (!this.isReady) {
|
|
3598
|
-
logger.debug("startConversation: skip
|
|
3637
|
+
logger.debug("startConversation: skip \u2014 not ready");
|
|
3599
3638
|
return;
|
|
3600
3639
|
}
|
|
3601
3640
|
if (this.isChatting || this.startConversationTimeout) {
|
|
3602
|
-
logger.debug("startConversation: skip
|
|
3641
|
+
logger.debug("startConversation: skip \u2014 chat in progress");
|
|
3603
3642
|
return;
|
|
3604
3643
|
}
|
|
3605
3644
|
const availability = await this.brain.getAvailability();
|
|
@@ -3613,11 +3652,11 @@ class BaseChannel {
|
|
|
3613
3652
|
if (count >= countThreshold)
|
|
3614
3653
|
return;
|
|
3615
3654
|
const nowMs = now.getTime();
|
|
3616
|
-
const history =
|
|
3655
|
+
const history = this.getMessageHistory(new Date(nowMs - 24 * 60 * 60 * 1000), now);
|
|
3617
3656
|
try {
|
|
3618
3657
|
const replies = await this.brain.sendMessage(history, [], {
|
|
3619
3658
|
initiate: true,
|
|
3620
|
-
send: this.
|
|
3659
|
+
send: this.sendAndRecord.bind(this)
|
|
3621
3660
|
});
|
|
3622
3661
|
if (replies.length === 0)
|
|
3623
3662
|
return;
|
|
@@ -3709,10 +3748,15 @@ class BaseChannel {
|
|
|
3709
3748
|
twoDaysAgo.setDate(now.getDate() - 2);
|
|
3710
3749
|
this.isSending = true;
|
|
3711
3750
|
try {
|
|
3712
|
-
|
|
3751
|
+
const history = this.getMessageHistory(twoDaysAgo, now);
|
|
3752
|
+
for (const m of newUserMessages)
|
|
3753
|
+
this.saveMessageHistory(m);
|
|
3754
|
+
await this.brain.sendMessage(history, newUserMessages, {
|
|
3755
|
+
send: this.sendAndRecord.bind(this)
|
|
3756
|
+
});
|
|
3713
3757
|
} catch (e) {
|
|
3714
3758
|
logger.error(`Error while sending message: ${e}`);
|
|
3715
|
-
logger.debug(`onMessage: sendMessage threw
|
|
3759
|
+
logger.debug(`onMessage: sendMessage threw \u2014 ${e instanceof Error ? e.stack : String(e)}`);
|
|
3716
3760
|
} finally {
|
|
3717
3761
|
this.isSending = false;
|
|
3718
3762
|
if (this.isSendingQueue.length > 0) {
|
|
@@ -3730,7 +3774,7 @@ class BaseChannel {
|
|
|
3730
3774
|
}
|
|
3731
3775
|
}, MESSAGE_DEBOUNCE_MS);
|
|
3732
3776
|
} else {
|
|
3733
|
-
logger.debug(`onMessage: isSending
|
|
3777
|
+
logger.debug(`onMessage: isSending \u2014 buffering into isSendingQueue (size=${this.isSendingQueue.length + 1})`);
|
|
3734
3778
|
this.isSendingQueue.push(message);
|
|
3735
3779
|
}
|
|
3736
3780
|
this.isChatting = true;
|
|
@@ -3756,7 +3800,7 @@ class BaseChannel {
|
|
|
3756
3800
|
const current = await this.brain.getAvailability();
|
|
3757
3801
|
const prev = this.previousAvailability;
|
|
3758
3802
|
this.previousAvailability = current.status;
|
|
3759
|
-
logger.debug(`availabilityWatcher: ${prev ?? "(initial)"}
|
|
3803
|
+
logger.debug(`availabilityWatcher: ${prev ?? "(initial)"} \u2192 ${current.status}`);
|
|
3760
3804
|
if (prev !== current.status) {
|
|
3761
3805
|
await this.setAvailability(current.status);
|
|
3762
3806
|
}
|
|
@@ -3772,7 +3816,7 @@ class BaseChannel {
|
|
|
3772
3816
|
}
|
|
3773
3817
|
const current = await this.brain.getAvailability();
|
|
3774
3818
|
if (current.status !== "online") {
|
|
3775
|
-
logger.debug(`flushDeferred: skip
|
|
3819
|
+
logger.debug(`flushDeferred: skip \u2014 still ${current.status} (deferred size=${this.deferredQueue.length})`);
|
|
3776
3820
|
return;
|
|
3777
3821
|
}
|
|
3778
3822
|
const drained = this.deferredQueue.splice(0, this.deferredQueue.length);
|
|
@@ -3888,6 +3932,12 @@ class BaseChannel {
|
|
|
3888
3932
|
}));
|
|
3889
3933
|
case "today-availability":
|
|
3890
3934
|
return await this.brain.getTodayScheduledAvailability(now);
|
|
3935
|
+
case "persona": {
|
|
3936
|
+
const stored = await this.brain.memory.get("persona");
|
|
3937
|
+
return stored?.content ?? null;
|
|
3938
|
+
}
|
|
3939
|
+
case "base-system-prompt":
|
|
3940
|
+
return this.brain.brainbase.baseSystemPrompt;
|
|
3891
3941
|
}
|
|
3892
3942
|
}
|
|
3893
3943
|
static async shutdownAll() {
|
|
@@ -3972,10 +4022,23 @@ class BaseChannel {
|
|
|
3972
4022
|
displayName: channel.brain.brainbase.displayName
|
|
3973
4023
|
};
|
|
3974
4024
|
}
|
|
4025
|
+
getMessageHistory(start, end) {
|
|
4026
|
+
return getMessageHistory(this.brain.brainbase.brainId, start, end);
|
|
4027
|
+
}
|
|
4028
|
+
saveMessageHistory(entry) {
|
|
4029
|
+
appendMessageHistory(this.brain.brainbase.brainId, entry);
|
|
4030
|
+
}
|
|
4031
|
+
async sendAndRecord(text) {
|
|
4032
|
+
await this.send(text);
|
|
4033
|
+
this.saveMessageHistory({
|
|
4034
|
+
sender: "persona",
|
|
4035
|
+
time: new Date,
|
|
4036
|
+
content: text
|
|
4037
|
+
});
|
|
4038
|
+
}
|
|
3975
4039
|
}
|
|
3976
4040
|
|
|
3977
4041
|
// src/channel/discord.ts
|
|
3978
|
-
var HISTORY_CAP = 1000;
|
|
3979
4042
|
var AVAILABILITY_STATUS_MAP = {
|
|
3980
4043
|
online: "online",
|
|
3981
4044
|
"do-not-disturb": "dnd",
|
|
@@ -3985,7 +4048,6 @@ var AVAILABILITY_STATUS_MAP = {
|
|
|
3985
4048
|
class DiscordChannel extends BaseChannel {
|
|
3986
4049
|
client;
|
|
3987
4050
|
targetChannel;
|
|
3988
|
-
history = [];
|
|
3989
4051
|
constructor(brain) {
|
|
3990
4052
|
super(brain);
|
|
3991
4053
|
}
|
|
@@ -4037,14 +4099,12 @@ class DiscordChannel extends BaseChannel {
|
|
|
4037
4099
|
this.onPairing(inbound);
|
|
4038
4100
|
return;
|
|
4039
4101
|
}
|
|
4040
|
-
|
|
4102
|
+
logger.debug(`MessageCreate: dispatching (channel=${msg.channelId})`);
|
|
4103
|
+
this.onMessage({
|
|
4041
4104
|
sender: "user",
|
|
4042
4105
|
time: msg.createdAt,
|
|
4043
4106
|
content
|
|
4044
|
-
};
|
|
4045
|
-
this.pushHistory(entry);
|
|
4046
|
-
logger.debug(`MessageCreate: stored in history, dispatching (channel=${msg.channelId})`);
|
|
4047
|
-
this.onMessage(entry);
|
|
4107
|
+
});
|
|
4048
4108
|
});
|
|
4049
4109
|
logger.debug(`DiscordChannel.init: logging in`);
|
|
4050
4110
|
await this.client.login(this.brain.brainbase.discord.token);
|
|
@@ -4076,7 +4136,7 @@ class DiscordChannel extends BaseChannel {
|
|
|
4076
4136
|
this.targetChannel = channel;
|
|
4077
4137
|
}
|
|
4078
4138
|
}
|
|
4079
|
-
logger.success(`Discord channel bound: ${this.brain.brainbase.displayName}
|
|
4139
|
+
logger.success(`Discord channel bound: ${this.brain.brainbase.displayName} \u2192 ${entry.channelId}`);
|
|
4080
4140
|
}
|
|
4081
4141
|
await super.completePairing(entry);
|
|
4082
4142
|
}
|
|
@@ -4101,17 +4161,9 @@ class DiscordChannel extends BaseChannel {
|
|
|
4101
4161
|
return;
|
|
4102
4162
|
}
|
|
4103
4163
|
const mapped = AVAILABILITY_STATUS_MAP[status];
|
|
4104
|
-
logger.debug(`setAvailability: ${status}
|
|
4164
|
+
logger.debug(`setAvailability: ${status} \u2192 ${mapped}`);
|
|
4105
4165
|
this.client.user.setStatus(mapped);
|
|
4106
4166
|
}
|
|
4107
|
-
async getMessageHistoryBetween(start, end) {
|
|
4108
|
-
return this.history.filter((m) => m.time >= start && m.time <= end);
|
|
4109
|
-
}
|
|
4110
|
-
pushHistory(entry) {
|
|
4111
|
-
this.history.push(entry);
|
|
4112
|
-
if (this.history.length > HISTORY_CAP)
|
|
4113
|
-
this.history.shift();
|
|
4114
|
-
}
|
|
4115
4167
|
async resolveSendChannel() {
|
|
4116
4168
|
if (this.targetChannel) {
|
|
4117
4169
|
logger.debug(`resolveSendChannel: cache hit`);
|
|
@@ -4160,12 +4212,9 @@ class DiscordChannel extends BaseChannel {
|
|
|
4160
4212
|
|
|
4161
4213
|
// src/channel/telegram.ts
|
|
4162
4214
|
import { Bot } from "gramio";
|
|
4163
|
-
var HISTORY_CAP2 = 1000;
|
|
4164
|
-
|
|
4165
4215
|
class TelegramChannel extends BaseChannel {
|
|
4166
4216
|
bot;
|
|
4167
4217
|
chatId;
|
|
4168
|
-
history = [];
|
|
4169
4218
|
constructor(brain) {
|
|
4170
4219
|
super(brain);
|
|
4171
4220
|
}
|
|
@@ -4207,14 +4256,12 @@ class TelegramChannel extends BaseChannel {
|
|
|
4207
4256
|
return;
|
|
4208
4257
|
}
|
|
4209
4258
|
this.chatId = ctx.chat.id;
|
|
4210
|
-
|
|
4259
|
+
logger.debug(`Telegram message: dispatching (chat=${ctx.chat.id})`);
|
|
4260
|
+
this.onMessage({
|
|
4211
4261
|
sender: "user",
|
|
4212
4262
|
time: inbound.time,
|
|
4213
4263
|
content: text
|
|
4214
|
-
};
|
|
4215
|
-
this.pushHistory(entry);
|
|
4216
|
-
logger.debug(`Telegram message: stored in history, dispatching (chat=${ctx.chat.id})`);
|
|
4217
|
-
this.onMessage(entry);
|
|
4264
|
+
});
|
|
4218
4265
|
});
|
|
4219
4266
|
logger.debug(`TelegramChannel.init: starting bot`);
|
|
4220
4267
|
await this.bot.start();
|
|
@@ -4236,7 +4283,7 @@ class TelegramChannel extends BaseChannel {
|
|
|
4236
4283
|
this.brain.brainbase.telegram.chatId = entry.chatId;
|
|
4237
4284
|
this.chatId = entry.chatId;
|
|
4238
4285
|
await this.brain.persistBrainBase();
|
|
4239
|
-
logger.success(`Telegram chat bound: ${this.brain.brainbase.displayName}
|
|
4286
|
+
logger.success(`Telegram chat bound: ${this.brain.brainbase.displayName} \u2192 ${entry.chatId}`);
|
|
4240
4287
|
}
|
|
4241
4288
|
await super.completePairing(entry);
|
|
4242
4289
|
}
|
|
@@ -4254,14 +4301,6 @@ class TelegramChannel extends BaseChannel {
|
|
|
4254
4301
|
async setAvailability(_status) {
|
|
4255
4302
|
logger.debug(`setAvailability: ${_status} (no-op, Telegram has no bot presence)`);
|
|
4256
4303
|
}
|
|
4257
|
-
async getMessageHistoryBetween(start, end) {
|
|
4258
|
-
return this.history.filter((m) => m.time >= start && m.time <= end);
|
|
4259
|
-
}
|
|
4260
|
-
pushHistory(entry) {
|
|
4261
|
-
this.history.push(entry);
|
|
4262
|
-
if (this.history.length > HISTORY_CAP2)
|
|
4263
|
-
this.history.shift();
|
|
4264
|
-
}
|
|
4265
4304
|
async teardownClient() {
|
|
4266
4305
|
if (!this.bot) {
|
|
4267
4306
|
logger.debug(`teardownClient: no bot, nothing to stop`);
|
|
@@ -4274,9 +4313,9 @@ class TelegramChannel extends BaseChannel {
|
|
|
4274
4313
|
}
|
|
4275
4314
|
|
|
4276
4315
|
// src/utils/daemonClient.ts
|
|
4277
|
-
import { connect } from "
|
|
4278
|
-
import { join as
|
|
4279
|
-
var DAEMON_SOCKET_PATH =
|
|
4316
|
+
import { connect } from "net";
|
|
4317
|
+
import { join as join5 } from "path";
|
|
4318
|
+
var DAEMON_SOCKET_PATH = join5(config.brainboxRoot, "daemon.sock");
|
|
4280
4319
|
async function sendToDaemon(payload) {
|
|
4281
4320
|
logger.debug(`sendToDaemon: command="${payload.command}" args=${JSON.stringify(payload.args) ?? "null"}`);
|
|
4282
4321
|
let reply;
|
|
@@ -4336,9 +4375,9 @@ function exchangeOnce(payload) {
|
|
|
4336
4375
|
}
|
|
4337
4376
|
|
|
4338
4377
|
// src/commands/daemon.ts
|
|
4339
|
-
import { createServer } from "
|
|
4340
|
-
import { chmodSync, unlinkSync } from "
|
|
4341
|
-
import { join as
|
|
4378
|
+
import { createServer } from "net";
|
|
4379
|
+
import { chmodSync, unlinkSync } from "fs";
|
|
4380
|
+
import { join as join6 } from "path";
|
|
4342
4381
|
|
|
4343
4382
|
// src/commands/daemon/commands.ts
|
|
4344
4383
|
var log8 = logger.child("daemon-cmd");
|
|
@@ -4362,7 +4401,7 @@ async function dispatch(payload) {
|
|
|
4362
4401
|
log8.debug(`dispatch: unknown command "${command}"`);
|
|
4363
4402
|
return { ok: false, error: `unknown command: ${command}` };
|
|
4364
4403
|
}
|
|
4365
|
-
log8.debug(`dispatch: "${command}"
|
|
4404
|
+
log8.debug(`dispatch: "${command}" \u2192 handler (args type=${typeof args})`);
|
|
4366
4405
|
try {
|
|
4367
4406
|
return await entry.handler(args);
|
|
4368
4407
|
} catch (error) {
|
|
@@ -4490,9 +4529,9 @@ async function startChannels() {
|
|
|
4490
4529
|
return started;
|
|
4491
4530
|
}
|
|
4492
4531
|
async function daemon() {
|
|
4493
|
-
const logDir =
|
|
4532
|
+
const logDir = join6(config.brainboxRoot, "logs");
|
|
4494
4533
|
logger.configure({ logDir });
|
|
4495
|
-
configureLlmLog(config.debug ?
|
|
4534
|
+
configureLlmLog(config.debug ? join6(logDir, "llm") : undefined);
|
|
4496
4535
|
logger.debug(`daemon: boot (debug=${config.debug}, logDir=${logDir}, llmLog=${config.debug})`);
|
|
4497
4536
|
const started = await startChannels();
|
|
4498
4537
|
if (started === 0) {
|
|
@@ -4608,7 +4647,7 @@ async function listBrains() {
|
|
|
4608
4647
|
}
|
|
4609
4648
|
}
|
|
4610
4649
|
async function setActivated(brainId, activated) {
|
|
4611
|
-
logger.debug(`setActivated: id=${brainId}
|
|
4650
|
+
logger.debug(`setActivated: id=${brainId} \u2192 ${activated}`);
|
|
4612
4651
|
const brain = await brainManager.loadBrain(brainId);
|
|
4613
4652
|
if (!brain) {
|
|
4614
4653
|
logger.error(`Brain not found: ${brainId}`);
|
|
@@ -4685,7 +4724,7 @@ async function viewThing(thing, brainId) {
|
|
|
4685
4724
|
args: { thing, brainId }
|
|
4686
4725
|
});
|
|
4687
4726
|
const name = response.result?.displayName ?? brainId;
|
|
4688
|
-
logger.info(`${thing}
|
|
4727
|
+
logger.info(`${thing} \u2014 "${name}" (${brainId})`);
|
|
4689
4728
|
console.log(JSON.stringify(response.result?.value ?? null, null, 2));
|
|
4690
4729
|
}
|
|
4691
4730
|
function register3(program) {
|
|
@@ -5255,7 +5294,7 @@ function ModelApp({
|
|
|
5255
5294
|
children: [
|
|
5256
5295
|
"Known providers: ",
|
|
5257
5296
|
listProviderNames().slice(0, 12).join(", "),
|
|
5258
|
-
"
|
|
5297
|
+
"\u2026"
|
|
5259
5298
|
]
|
|
5260
5299
|
}, undefined, true, undefined, this)
|
|
5261
5300
|
]
|
|
@@ -5473,7 +5512,7 @@ function RawSelect({
|
|
|
5473
5512
|
filtered.length,
|
|
5474
5513
|
"/",
|
|
5475
5514
|
items.length,
|
|
5476
|
-
")
|
|
5515
|
+
") \xB7 \u2191\u2193 \xB7 type to filter \xB7 enter"
|
|
5477
5516
|
]
|
|
5478
5517
|
}, undefined, true, undefined, this),
|
|
5479
5518
|
filtered.length === 0 ? /* @__PURE__ */ jsxDEV4(Text4, {
|
|
@@ -5483,7 +5522,7 @@ function RawSelect({
|
|
|
5483
5522
|
children: [
|
|
5484
5523
|
start > 0 && /* @__PURE__ */ jsxDEV4(Text4, {
|
|
5485
5524
|
dimColor: true,
|
|
5486
|
-
children: "
|
|
5525
|
+
children: " \u2026"
|
|
5487
5526
|
}, undefined, false, undefined, this),
|
|
5488
5527
|
visible.map((item, i) => {
|
|
5489
5528
|
const idx = start + i;
|
|
@@ -5498,7 +5537,7 @@ function RawSelect({
|
|
|
5498
5537
|
}),
|
|
5499
5538
|
start + visible.length < filtered.length && /* @__PURE__ */ jsxDEV4(Text4, {
|
|
5500
5539
|
dimColor: true,
|
|
5501
|
-
children: "
|
|
5540
|
+
children: " \u2026"
|
|
5502
5541
|
}, undefined, false, undefined, this)
|
|
5503
5542
|
]
|
|
5504
5543
|
}, undefined, true, undefined, this)
|
|
@@ -5520,7 +5559,7 @@ function Select(props) {
|
|
|
5520
5559
|
// src/commands/onboard.tsx
|
|
5521
5560
|
import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
|
|
5522
5561
|
function ok(msg) {
|
|
5523
|
-
console.log(chalk3.green(
|
|
5562
|
+
console.log(chalk3.green(`\u2714 ${msg}`));
|
|
5524
5563
|
}
|
|
5525
5564
|
function info(msg) {
|
|
5526
5565
|
console.log(msg);
|
|
@@ -5545,12 +5584,12 @@ function ProviderApp({
|
|
|
5545
5584
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5546
5585
|
children: [
|
|
5547
5586
|
chalk3.bold("Step 1/4"),
|
|
5548
|
-
"
|
|
5587
|
+
" \u2014 Choose your first provider"
|
|
5549
5588
|
]
|
|
5550
5589
|
}, undefined, true, undefined, this),
|
|
5551
5590
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5552
5591
|
dimColor: true,
|
|
5553
|
-
children: "
|
|
5592
|
+
children: "\u2191\u2193 to move, type to filter, enter to select"
|
|
5554
5593
|
}, undefined, false, undefined, this),
|
|
5555
5594
|
/* @__PURE__ */ jsxDEV5(Select, {
|
|
5556
5595
|
prompt: "provider> ",
|
|
@@ -5575,7 +5614,7 @@ function ProviderApp({
|
|
|
5575
5614
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5576
5615
|
children: [
|
|
5577
5616
|
chalk3.bold("Step 1/4"),
|
|
5578
|
-
"
|
|
5617
|
+
" \u2014 ",
|
|
5579
5618
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5580
5619
|
color: "cyan",
|
|
5581
5620
|
children: stage.provider
|
|
@@ -5629,7 +5668,7 @@ function ProviderApp({
|
|
|
5629
5668
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5630
5669
|
children: [
|
|
5631
5670
|
chalk3.bold("Step 1/4"),
|
|
5632
|
-
"
|
|
5671
|
+
" \u2014 ",
|
|
5633
5672
|
stage.provider,
|
|
5634
5673
|
" extra:",
|
|
5635
5674
|
" ",
|
|
@@ -5675,7 +5714,7 @@ function ModelApp2({
|
|
|
5675
5714
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5676
5715
|
children: [
|
|
5677
5716
|
chalk3.bold("Step 1/4"),
|
|
5678
|
-
"
|
|
5717
|
+
" \u2014 Default model (both slots)"
|
|
5679
5718
|
]
|
|
5680
5719
|
}, undefined, true, undefined, this),
|
|
5681
5720
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
@@ -5689,7 +5728,7 @@ function ModelApp2({
|
|
|
5689
5728
|
"/"
|
|
5690
5729
|
]
|
|
5691
5730
|
}, undefined, true, undefined, this),
|
|
5692
|
-
"model
|
|
5731
|
+
"model \u2014 fine-tune later with ",
|
|
5693
5732
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5694
5733
|
color: "cyan",
|
|
5695
5734
|
children: "brainbox model"
|
|
@@ -5732,7 +5771,7 @@ function SuperMemoryApp({
|
|
|
5732
5771
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5733
5772
|
children: [
|
|
5734
5773
|
chalk3.bold("Step 2/4"),
|
|
5735
|
-
"
|
|
5774
|
+
" \u2014 Supermemory API key"
|
|
5736
5775
|
]
|
|
5737
5776
|
}, undefined, true, undefined, this),
|
|
5738
5777
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
@@ -5771,7 +5810,7 @@ function BrainApp({
|
|
|
5771
5810
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5772
5811
|
children: [
|
|
5773
5812
|
chalk3.bold("Step 3/4"),
|
|
5774
|
-
"
|
|
5813
|
+
" \u2014 Brain name"
|
|
5775
5814
|
]
|
|
5776
5815
|
}, undefined, true, undefined, this),
|
|
5777
5816
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
@@ -5804,7 +5843,7 @@ function BrainApp({
|
|
|
5804
5843
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5805
5844
|
children: [
|
|
5806
5845
|
chalk3.bold("Step 3/4"),
|
|
5807
|
-
"
|
|
5846
|
+
" \u2014 Language for",
|
|
5808
5847
|
" ",
|
|
5809
5848
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5810
5849
|
color: "cyan",
|
|
@@ -5814,7 +5853,7 @@ function BrainApp({
|
|
|
5814
5853
|
}, undefined, true, undefined, this),
|
|
5815
5854
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5816
5855
|
dimColor: true,
|
|
5817
|
-
children: "
|
|
5856
|
+
children: "\u2191\u2193 to move, type to filter, enter to select"
|
|
5818
5857
|
}, undefined, false, undefined, this),
|
|
5819
5858
|
/* @__PURE__ */ jsxDEV5(Select, {
|
|
5820
5859
|
prompt: "language> ",
|
|
@@ -5857,7 +5896,7 @@ function BrainApp({
|
|
|
5857
5896
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5858
5897
|
children: [
|
|
5859
5898
|
chalk3.bold("Step 3/4"),
|
|
5860
|
-
"
|
|
5899
|
+
" \u2014 Seed for",
|
|
5861
5900
|
" ",
|
|
5862
5901
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5863
5902
|
color: "cyan",
|
|
@@ -5880,7 +5919,7 @@ function BrainApp({
|
|
|
5880
5919
|
}, undefined, false, undefined, this),
|
|
5881
5920
|
busy ? /* @__PURE__ */ jsxDEV5(Text5, {
|
|
5882
5921
|
dimColor: true,
|
|
5883
|
-
children: "Creating brain
|
|
5922
|
+
children: "Creating brain\u2026 (This can take few minutes depending on the model's response speed)"
|
|
5884
5923
|
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV5(TextInput, {
|
|
5885
5924
|
prompt: "seed> ",
|
|
5886
5925
|
onSubmit: (raw) => {
|
|
@@ -5931,7 +5970,7 @@ function ChannelApp({
|
|
|
5931
5970
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5932
5971
|
children: [
|
|
5933
5972
|
chalk3.bold("Step 4/4"),
|
|
5934
|
-
"
|
|
5973
|
+
" \u2014 Channel for",
|
|
5935
5974
|
" ",
|
|
5936
5975
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5937
5976
|
color: "cyan",
|
|
@@ -5941,7 +5980,7 @@ function ChannelApp({
|
|
|
5941
5980
|
}, undefined, true, undefined, this),
|
|
5942
5981
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5943
5982
|
dimColor: true,
|
|
5944
|
-
children: "
|
|
5983
|
+
children: "\u2191\u2193 to move, type to filter, enter to select"
|
|
5945
5984
|
}, undefined, false, undefined, this),
|
|
5946
5985
|
/* @__PURE__ */ jsxDEV5(Select, {
|
|
5947
5986
|
prompt: "channel> ",
|
|
@@ -5969,7 +6008,7 @@ function ChannelApp({
|
|
|
5969
6008
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
5970
6009
|
children: [
|
|
5971
6010
|
chalk3.bold("Step 4/4"),
|
|
5972
|
-
"
|
|
6011
|
+
" \u2014 ",
|
|
5973
6012
|
stage.kind_,
|
|
5974
6013
|
" bot token"
|
|
5975
6014
|
]
|
|
@@ -5999,7 +6038,7 @@ function ChannelApp({
|
|
|
5999
6038
|
/* @__PURE__ */ jsxDEV5(Text5, {
|
|
6000
6039
|
children: [
|
|
6001
6040
|
chalk3.bold("Step 4/4"),
|
|
6002
|
-
"
|
|
6041
|
+
" \u2014 Optional",
|
|
6003
6042
|
" ",
|
|
6004
6043
|
stage.kind_ === "discord" ? "channelId" : "chatId",
|
|
6005
6044
|
" (blank = pair later)"
|
|
@@ -6040,7 +6079,7 @@ function ChannelApp({
|
|
|
6040
6079
|
};
|
|
6041
6080
|
}
|
|
6042
6081
|
await brainManager.saveBrain(brainId, updated);
|
|
6043
|
-
onDone(`Bound ${displayName}
|
|
6082
|
+
onDone(`Bound ${displayName} \u2192 ${stage.kind_}${target ? ` (${target})` : " (pairing mode)"}`);
|
|
6044
6083
|
})();
|
|
6045
6084
|
}
|
|
6046
6085
|
}, undefined, false, undefined, this),
|
|
@@ -6053,7 +6092,7 @@ function ChannelApp({
|
|
|
6053
6092
|
}
|
|
6054
6093
|
async function runOnboard() {
|
|
6055
6094
|
console.clear();
|
|
6056
|
-
info(`Welcome
|
|
6095
|
+
info(`Welcome \u2014 let's get ${chalk3.bold("brainbox")} ready.`);
|
|
6057
6096
|
const providers = listProviderNames().slice().sort();
|
|
6058
6097
|
const { promise, resolve: resolve2 } = Promise.withResolvers();
|
|
6059
6098
|
const active = { current: null };
|
|
@@ -6114,7 +6153,7 @@ logger.configure({ level: config.debug ? "debug" : "info" });
|
|
|
6114
6153
|
logger.debug(`brainbox starting (debug=${config.debug}, root=${config.brainboxRoot})`);
|
|
6115
6154
|
function getVersion() {
|
|
6116
6155
|
try {
|
|
6117
|
-
const pkgPath =
|
|
6156
|
+
const pkgPath = join7(__dirname3, "..", "package.json");
|
|
6118
6157
|
const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
6119
6158
|
return pkg.version ?? "0.0.0";
|
|
6120
6159
|
} catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@p-sw/brainbox",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3-beta.1",
|
|
4
4
|
"module": "dist/index.js",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -10,10 +10,11 @@
|
|
|
10
10
|
"scripts": {
|
|
11
11
|
"cli": "bun run src/index.ts",
|
|
12
12
|
"format": "bun run prettier 'src/**/*.ts' -w",
|
|
13
|
-
"build": "rm -rf dist && bun build ./src/index.ts --outdir dist --target
|
|
13
|
+
"build": "rm -rf dist && bun build ./src/index.ts --outdir dist --target bun --packages external && sed -i '1s|#!/usr/bin/env node|#!/usr/bin/env bun|' dist/index.js && cp -r prompts dist/prompts && chmod +x dist/index.js",
|
|
14
14
|
"prepack": "bun run build"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
|
+
"@types/bun": "^1.3.14",
|
|
17
18
|
"@types/node": "^25.9.1"
|
|
18
19
|
},
|
|
19
20
|
"peerDependencies": {
|