Table of Contents
- API Reference
- Persona
- Constructors
- ready()
- createDailySchedule(datetime, message)
- createMonthlySchedule(datetime, message)
- deleteSchedulesBefore(cutoffExclusive)
- deleteSchedulesOlderThan(datetime)
- getTodayScheduledAvailability(datetime)
- sendMessage(input)
- startConversation(input)
- sleepMemory(input)
- Options and models
- PersonaOptions
- PersonaModels
- PersonaInitializationModel
- ConversationModel
- ReplyGenerationInput
- OutgoingMessageDraft
- RewriteModel
- MemoryExtractionModel
- Memory stores
- BoxBrainMemoryStore
- InMemoryMemoryStore
- IdentityDbMemoryStore
- createSqliteIdentityMemoryStore(filename)
- Core types
- DateTimeInput
- MemorySpace
- FactDraft
- StoredFact
- PersonaMessage
- ScheduleActivity
- ScheduleEntry
- AvailabilityMode
- AvailabilityRange
- ScheduledAvailabilitySnapshot
- MandatoryConversationContext
- Schedule helper exports
- Conversation helper exports
API Reference
This reference describes the public exports visible from src/index.ts:
export * from './types';
export * from './memory';
export * from './schedule';
export * from './conversation';
export * from './persona';
Persona
Main framework class.
Constructors
new Persona(displayName: string, seedMessage: string, options?: PersonaOptions)
new Persona(spaceId: string, options?: PersonaOptions)
Create mode:
const persona = new Persona('Mina', 'Mina likes quiet cafes.', { memory, models });
Load mode:
const persona = new Persona(existingSpaceId, { memory, models });
Behavior:
- Create mode calls
memory.createSpace({ displayName, seedMessage, now }). - Load mode calls
memory.getSpace(spaceId)and throws if missing. - Initialization starts immediately in the constructor and is awaited through
ready(). - If
options.memoryis omitted, anInMemoryMemoryStoreis used.
ready()
ready(): Promise<MemorySpace>
Returns the created or loaded persona memory space.
createDailySchedule(datetime, message)
createDailySchedule(datetime: DateTimeInput, message: string): Promise<ScheduleEntry[]>
Creates tomorrow's 10-minute schedule.
If datetime is May 1, the schedule covers May 2 00:00 through May 3 00:00 and contains 144 entries.
Side effects:
- saves entries through
memory.saveScheduleEntries - refreshes the in-memory availability snapshot
- emits
persona.schedule.daily.generated
Default deterministic mapping:
- message containing travel/trip/여행 -> daytime
travel - message containing study/exam/공부/시험 -> daytime
study - message containing job/취업/구직 -> daytime
job-search - message containing work/일/회사 -> daytime
work - otherwise daytime defaults to
work
createMonthlySchedule(datetime, message)
createMonthlySchedule(datetime: DateTimeInput, message: string): Promise<ScheduleEntry[]>
Creates 30 day-level entries starting tomorrow.
Side effects:
- saves entries through
memory.saveScheduleEntries - refreshes availability
- emits
persona.schedule.monthly.generated
deleteSchedulesBefore(cutoffExclusive)
deleteSchedulesBefore(cutoffExclusive: DateTimeInput): Promise<number>
Deletes or marks schedule entries before a caller-provided cutoff.
Behavior depends on the memory store:
InMemoryMemoryStorephysically removes entries whoseendAt <= cutoffExclusiveand returns the deleted count.IdentityDbMemoryStoredoes not physically delete schedule facts through IdentityDB's public append-oriented API, so it returns0at the store layer.- The
Personalayer always records a fact with topicpersona.schedule.deletedand sourceboxbrain.schedule.prune.
deleteSchedulesOlderThan(datetime)
deleteSchedulesOlderThan(datetime: DateTimeInput): Promise<number>
Alias for deleteSchedulesBefore(datetime).
getTodayScheduledAvailability(datetime)
getTodayScheduledAvailability(datetime: DateTimeInput): Promise<ScheduledAvailabilitySnapshot>
Returns the cached schedule-derived availability snapshot.
Window rule:
- start: UTC start of
datetime's day - end: two UTC days later
If the snapshot is missing or the day changed, BoxBrain reloads schedule entries from memory and rebuilds the snapshot.
sendMessage(input)
sendMessage(input: {
datetime: DateTimeInput;
messageHistory: PersonaMessage[];
getLatestMessageHistory?: () => Promise<PersonaMessage[]>;
}): Promise<OutgoingMessageDraft>
Generates a reply to the user's message history.
Requires:
options.models.conversation
Pipeline:
- Await persona readiness.
- Load active availability.
- Build mandatory conversation context:
- formatted
messageHistory - schedule entries from yesterday through tomorrow
- availability snapshot
- facts tagged with
persona, the persona display name, oruser
- formatted
- Emit
persona.conversation.context.loaded. - Call
models.conversation.generateReplywith modereply. - Trim blank messages from the draft.
- If
getLatestMessageHistoryandmodels.rewriteare present, check whether a newer history arrived and optionally rewrite. - Emit
persona.conversation.reply.generated. - Return
OutgoingMessageDraft.
Throws if no conversation model is configured.
startConversation(input)
startConversation(input: {
datetime: DateTimeInput;
messageHistory: PersonaMessage[];
}): Promise<OutgoingMessageDraft>
Generates a proactive opener from the persona.
Requires:
options.models.conversation
Uses the same mandatory context pipeline as sendMessage, but passes mode: 'start-conversation' to the model.
Emits persona.conversation.started.
sleepMemory(input)
sleepMemory(input: {
datetime: DateTimeInput;
messageHistory: PersonaMessage[];
}): Promise<FactDraft[]>
Extracts durable facts from a period of messages and persists them.
Requires:
options.models.memoryExtraction
Pipeline:
- Find relevant context facts tagged with
persona, the persona display name, oruser. - Format the provided message history.
- Call
models.memoryExtraction.extractwith the objectivization instruction. - Persist each returned fact through
memory.addFact. - Add the topic
sleepMemoryto each persisted fact. - Use source
boxbrain.sleepMemoryif the draft did not specify a source. - Emit
persona.memory.sleep.persisted. - Return the extracted drafts.
Recommended cadence: daily around midnight, passing the previous day's messages.
Options and models
PersonaOptions
interface PersonaOptions {
memory?: BoxBrainMemoryStore;
models?: PersonaModels;
debug?: DebugHook;
now?: DateTimeInput;
}
memory: storage adapter. Defaults toInMemoryMemoryStore.models: provider-agnostic LLM adapters.debug: optional async event hook.now: deterministic initialization time for tests or replay.
PersonaModels
interface PersonaModels {
initialization?: PersonaInitializationModel;
conversation?: ConversationModel;
rewrite?: RewriteModel;
memoryExtraction?: MemoryExtractionModel;
}
PersonaInitializationModel
interface PersonaInitializationModel {
extractInitialFacts(input: {
displayName: string;
seedMessage: string;
now: string;
}): Promise<FactDraft[]>;
}
Called during persona creation. If omitted, BoxBrain stores a default seed fact. If it returns [], BoxBrain stores no fallback fact.
ConversationModel
interface ConversationModel {
generateReply(input: ReplyGenerationInput): Promise<OutgoingMessageDraft>;
}
ReplyGenerationInput
interface ReplyGenerationInput {
persona: MemorySpace;
now: string;
mode: 'reply' | 'start-conversation';
context: MandatoryConversationContext;
userMessage?: string;
instruction: string;
}
OutgoingMessageDraft
interface OutgoingMessageDraft {
messages: string[];
reasoning?: string;
}
The host app should deliver each messages[] item as a separate messenger message.
RewriteModel
interface RewriteModel {
decide(input: RewriteDecisionInput): Promise<RewriteDecision>;
}
interface RewriteDecision {
rewrite: boolean;
draft?: OutgoingMessageDraft;
reason?: string;
}
MemoryExtractionModel
interface MemoryExtractionModel {
extract(input: MemoryExtractionInput): Promise<FactDraft[]>;
}
interface MemoryExtractionInput {
persona: MemorySpace;
now: string;
formattedMessageHistory: string;
contextFacts: StoredFact[];
instruction: string;
}
Memory stores
BoxBrainMemoryStore
interface BoxBrainMemoryStore {
createSpace(input: { displayName: string; seedMessage: string; now: string }): Promise<MemorySpace>;
getSpace(spaceId: string): Promise<MemorySpace | null>;
addFact(spaceId: string, fact: FactDraft): Promise<StoredFact>;
listFacts(spaceId: string): Promise<StoredFact[]>;
findFacts(spaceId: string, topics: string[]): Promise<StoredFact[]>;
saveScheduleEntries(spaceId: string, entries: ScheduleEntry[]): Promise<void>;
listScheduleEntries(spaceId: string, fromInclusive: string, toExclusive: string): Promise<ScheduleEntry[]>;
deleteScheduleEntriesBefore(spaceId: string, cutoffExclusive: string): Promise<number>;
}
InMemoryMemoryStore
Test/demo store backed by maps:
spaces: Map<string, MemorySpace>facts: Map<string, StoredFact[]>schedules: Map<string, ScheduleEntry[]>
Useful for unit tests and demos. Not persistent.
IdentityDbMemoryStore
Persistent store backed by IdentityDB.
import { IdentityDB } from 'identitydb';
import { IdentityDbMemoryStore } from 'boxbrain';
const db = await IdentityDB.connect({ client: 'sqlite', filename: '.data/personas.sqlite' });
await db.initialize();
const memory = new IdentityDbMemoryStore({ db });
createSqliteIdentityMemoryStore(filename)
createSqliteIdentityMemoryStore(filename: string): Promise<IdentityDbMemoryStore>
Convenience helper that connects and initializes an IdentityDB SQLite database.
Core types
DateTimeInput
type DateTimeInput = Date | string | number;
All datetime inputs are converted through new Date(...). Invalid dates throw.
MemorySpace
interface MemorySpace {
id: string;
displayName: string;
createdAt: string;
metadata: Record<string, unknown>;
}
FactDraft
interface FactDraft {
statement: string;
topics: string[];
confidence?: number;
source?: string;
metadata?: Record<string, unknown>;
}
StoredFact
interface StoredFact extends FactDraft {
id: string;
createdAt: string;
}
PersonaMessage
interface PersonaMessage {
sender: 'persona' | 'user';
time: DateTimeInput;
content: string;
}
ScheduleActivity
type ScheduleActivity =
| 'sleep'
| 'rest'
| 'meal'
| 'commute'
| 'work'
| 'study'
| 'job-search'
| 'travel'
| 'exercise'
| 'social'
| 'errand'
| 'free-time';
ScheduleEntry
interface ScheduleEntry {
id: string;
spaceId: string;
startAt: string;
endAt: string;
activity: ScheduleActivity;
title: string;
description?: string;
granularity: 'day' | 'ten-minute';
sourceMessage?: string;
metadata: Record<string, unknown>;
}
AvailabilityMode
type AvailabilityMode = 'online' | 'do-not-disturb' | 'offline';
AvailabilityRange
interface AvailabilityRange {
startAt: string;
endAt: string;
mode: AvailabilityMode;
sourceScheduleIds: string[];
reason: string;
}
ScheduledAvailabilitySnapshot
interface ScheduledAvailabilitySnapshot {
generatedAt: string;
windowStartAt: string;
windowEndAt: string;
ranges: AvailabilityRange[];
}
MandatoryConversationContext
interface MandatoryConversationContext {
formattedMessageHistory: string;
conversationWindowLabel: string;
memorySummary: string;
personaAndUserFacts: StoredFact[];
scheduleEntries: ScheduleEntry[];
availability: ScheduledAvailabilitySnapshot;
}
Schedule helper exports
The following helpers are exported for applications and tests:
toDate(input)
toIso(input)
startOfUtcDay(input)
addUtcDays(input, days)
scheduleTargetDay(now)
createTenMinuteDailySchedule(input)
createMonthlyScheduleEntries(input)
availabilityModeForEntry(entry)
buildAvailabilitySnapshot(input)
dateKeysAround(input)
Conversation helper exports
formatMessageHistory({ personaName, messages })
conversationInstruction()
memoryExtractionInstruction(now)
buildMandatoryConversationContext(input)
formatMessageHistory converts structured messages into model-readable text:
Mina@2026-04-30T23:00:00.000Z: See you later.
user@2026-05-01T12:00:00.000Z: What are you doing?