This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Getting Started
Setup options
BoxBrain now consumes the published identitydb package from npm, so a sibling checkout of IdentityDB is no longer required for normal usage or local verification.
Use BoxBrain in your own project
bun add boxbrain identitydb
Work on the BoxBrain repository itself
git clone https://git.psw.kr/p-sw/BoxBrain.git
cd BoxBrain
bun install
Verify the repository
bun run test
bun run check
bun run build
If you hit a better-sqlite3 native module / NODE_MODULE_VERSION mismatch after environment changes, reset the install so Bun can re-run trusted lifecycle scripts:
rm -rf node_modules bun.lock
bun install
Runtime requirements
You need:
- Node.js 20+
- Bun
- an initialized IdentityDB database connection at runtime
Create an IdentityDB instance
BoxBrain uses IdentityDB as the persistence layer.
import { IdentityDB } from 'identitydb';
const db = await IdentityDB.connect({
client: 'sqlite',
filename: ':memory:',
});
await db.initialize();
For a persistent local database, replace ':memory:' with a file path.
Create model adapters
BoxBrain expects provider adapters instead of hard-coding a vendor into the core runtime.
Provider-agnostic contracts
The package exports these adapter interfaces:
TextModelAdapterStructuredModelAdapterImageModelAdapterSpecialDateProvider
Quick start with Grok
import { createGrokAdapters } from 'boxbrain';
const grok = createGrokAdapters({
apiKey: process.env.XAI_API_KEY!,
textModel: 'grok-4.3-mini',
structuredModel: 'grok-4.3',
imageModel: 'grok-imagine-image-quality',
});
Initialize a persona
You can use either the functional helper or the class-based service entrypoint.
Functional helper
import { IdentityDB } from 'identitydb';
import { createGrokAdapters, initializePersona } from 'boxbrain';
const db = await IdentityDB.connect({ client: 'sqlite', filename: ':memory:' });
await db.initialize();
const grok = createGrokAdapters({
apiKey: process.env.XAI_API_KEY!,
textModel: 'grok-4.3-mini',
structuredModel: 'grok-4.3',
imageModel: 'grok-imagine-image-quality',
});
const persona = await initializePersona(db, {
displayName: 'Mina',
seedText:
'Mina is a thoughtful, witty, introverted, and emotionally observant product designer. She was raised in Busan and later moved to Seoul for work. She values loyalty, self-respect, and quiet consistency, loves late-night walks, indie music, and quiet cafés, dislikes performative networking and loud restaurants, and stays especially close to her older brother Jisoo, who is protective but teasing.',
currentDate: '2026-05-11',
structuredModel: grok.structured,
imageModel: grok.image,
generateProfileImage: true,
});
Class-based service
import { IdentityDB } from 'identitydb';
import { createGrokAdapters, PersonaService } from 'boxbrain';
const db = await IdentityDB.connect({ client: 'sqlite', filename: ':memory:' });
await db.initialize();
const grok = createGrokAdapters({
apiKey: process.env.XAI_API_KEY!,
textModel: 'grok-4.3-mini',
structuredModel: 'grok-4.3',
imageModel: 'grok-imagine-image-quality',
});
const personas = new PersonaService(db);
const persona = await personas.initialize({
displayName: 'Mina',
seedText: 'Mina is a thoughtful, witty Seoul product designer who loves quiet cafés.',
structuredModel: grok.structured,
});
Generate a schedule
import { generateSchedule } from 'boxbrain';
const schedule = await generateSchedule(db, {
spaceName: persona.spaceName,
displayName: persona.displayName,
currentDate: '2026-05-11',
scope: 'week',
timezone: 'Asia/Seoul',
structuredModel: grok.structured,
specialDateProvider: {
async listSpecialDates() {
return [
{
date: '2026-05-15',
title: 'Teacher’s Day',
description: 'A recurring Korean commemorative day.',
},
];
},
},
});
console.log(schedule.events);
console.log(schedule.availabilityEntries);
Read or override availability
import { getAvailabilitySnapshot, setAvailabilityStatus } from 'boxbrain';
await setAvailabilityStatus(db, {
spaceName: persona.spaceName,
mode: 'do_not_disturb',
reason: 'studying for an exam',
effectiveFrom: '2026-05-11T19:00:00.000Z',
until: '2026-05-11T22:00:00.000Z',
sourceType: 'manual',
});
const snapshot = await getAvailabilitySnapshot(db, {
spaceName: persona.spaceName,
at: '2026-05-11T20:00:00.000Z',
});
console.log(snapshot.current);
console.log(snapshot.next);
Run a conversation turn
BoxBrain separates memory retrieval and response planning into three structured model roles:
mandatoryMemoryModelcontextualMemoryModelresponseModel
You can point all three at the same underlying model if you want.
import { replyToConversation, startConversation } from 'boxbrain';
const proactive = await startConversation(db, {
spaceName: persona.spaceName,
counterpartId: 'user:shinwoo',
counterpartDisplayName: 'Shinwoo',
currentTime: '2026-05-11T12:00:00.000Z',
mandatoryMemoryModel: grok.structured,
contextualMemoryModel: grok.structured,
responseModel: grok.structured,
});
console.log(proactive.messages);
const reply = await replyToConversation(db, {
spaceName: persona.spaceName,
counterpartId: 'user:shinwoo',
counterpartDisplayName: 'Shinwoo',
currentTime: '2026-05-11T12:05:00.000Z',
message: 'Are you free tonight?',
mandatoryMemoryModel: grok.structured,
contextualMemoryModel: grok.structured,
responseModel: grok.structured,
});
console.log(reply.blocked);
console.log(reply.messages);
console.log(reply.usedMemories);
console.log(reply.toolCallsExecuted);
Timing helpers
import {
ONLINE_AVAILABILITY,
createReplyDelay,
createTypingDelay,
} from 'boxbrain';
const replyDelay = createReplyDelay(ONLINE_AVAILABILITY, {
isFirstReplyInExchange: true,
});
const typingDelay = createTypingDelay('지금 뭐해?', {
minSecondsPerCharacter: 0.05,
maxSecondsPerCharacter: 0.08,
});
Important caveats
1. BoxBrain is a library, not an HTTP service
You call the exported TypeScript APIs directly.
2. IdentityDB is a required runtime dependency
The framework assumes IdentityDB is available and initialized.
3. Internal source folders are a repository concern
The repository is organized by domain folders under src/, but the published package is still consumed through the package root exports.
4. Clean Bun installs depend on trusted lifecycle scripts
The repository keeps trustedDependencies for better-sqlite3 and esbuild so native/runtime build steps succeed during clean installs.