docs: refresh wiki for current runtime structure

2026-05-11 19:50:13 +09:00
parent da2200ad2a
commit 3a78db2b59
6 changed files with 301 additions and 249 deletions

@@ -1,32 +1,41 @@
# API Reference
This page documents the current public surface exported from the BoxBrain package root.
This page documents the **current public surface exported from the BoxBrain package root**.
```ts
export * from './adapters';
export * from './core/adapters';
export * from './core/types';
export * from './availability';
export * from './conversation';
export * from './grok';
export * from './memory';
export * from './persona';
export * from './providers/grok';
export * from './schedule';
export * from './timing';
export * from './types';
```
## Public API shape
The package root exposes both:
- **functional helpers** for direct use in applications
- **class-based service entrypoints** for clearer domain grouping in stateful integrations
## `persona`
### `PersonaService`
Class-based entrypoint for persona initialization.
### `initializePersona(db, input)`
Creates and persists a new persona runtime profile.
**Signature**
```ts
initializePersona(db: IdentityDB, input: InitializePersonaInput): Promise<InitializedPersona>
```
**Important input fields**
Important input fields:
- `displayName: string`
- `seedText?: string`
@@ -42,36 +51,18 @@ initializePersona(db: IdentityDB, input: InitializePersonaInput): Promise<Initia
- `generateProfileImage?: boolean`
- `reuseExistingSpace?: boolean`
`seedText` is the preferred initialize input shape: a single long freeform string that may already include personality, history, values, preferences, dislikes, and relationships. Structured fields remain available as optional supplemental hints and for backward compatibility.
**Behavior**
- generates a biography through `structuredModel`, using `seedText` as the primary source when provided
- extracts fact drafts through `structuredModel`
- writes biography facts into the persona space
- optionally generates a profile image and writes a profile-image fact
- creates a collision-resistant persona ID if one is not provided
**Returns**
```ts
interface InitializedPersona {
id: string;
spaceName: string;
displayName: string;
biography: string;
profileImageUrl?: string;
}
```
`seedText` is the preferred initialization input shape: one long freeform string containing the persona's personality, history, values, preferences, and relationships when available.
## `schedule`
### `ScheduleService`
Class-based entrypoint for schedule generation, listing, and pruning.
### `generateSchedule(db, input)`
Generates and persists schedule events, then derives schedule-backed availability entries.
**Signature**
```ts
generateSchedule(
db: IdentityDB,
@@ -79,38 +70,18 @@ generateSchedule(
): Promise<{ events: BoxBrainScheduleEvent[]; availabilityEntries: BoxBrainAvailabilityEntry[] }>
```
**Important input fields**
- `spaceName: string`
- `displayName?: string`
- `currentDate: string`
- `scope: 'day' | 'week' | 'month'`
- `timezone?: string`
- `structuredModel: StructuredModelAdapter`
- `specialDateProvider?: SpecialDateProvider`
### `listScheduleEvents(db, input)`
Lists persisted schedule events, excluding ones hidden by deletion markers.
**Signature**
```ts
listScheduleEvents(db: IdentityDB, input: ListScheduleEventsInput): Promise<BoxBrainScheduleEvent[]>
```
**Filters**
- `spaceName`
- optional `from`
- optional `until`
### `pruneExpiredSchedule(db, input)`
Marks schedule events as deleted when they ended before `referenceTime` minus an optional grace window.
**Signature**
```ts
pruneExpiredSchedule(db: IdentityDB, input: PruneExpiredScheduleInput): Promise<SchedulePruneResult>
```
@@ -119,58 +90,27 @@ pruneExpiredSchedule(db: IdentityDB, input: PruneExpiredScheduleInput): Promise<
Marks schedule events as deleted when they start before a cutoff timestamp.
**Signature**
```ts
pruneScheduleBefore(db: IdentityDB, input: PruneScheduleBeforeInput): Promise<SchedulePruneResult>
```
**Common types**
```ts
type BoxBrainScheduleScope = 'day' | 'week' | 'month';
type BoxBrainScheduleEventKind = 'routine' | 'special';
interface BoxBrainScheduleEvent {
id: string;
title: string;
description?: string;
startAt: string;
endAt: string;
availabilityMode: BoxBrainAvailabilityMode;
availabilityReason?: string;
kind: BoxBrainScheduleEventKind;
topics: BoxBrainTopicDraft[];
metadata?: JsonValue | null;
}
```
## `availability`
### `AvailabilityService`
Class-based entrypoint for availability writes, listing, and snapshot queries.
### `setAvailabilityStatus(db, input)`
Persists an explicit availability entry.
**Signature**
```ts
setAvailabilityStatus(db: IdentityDB, input: SetAvailabilityStatusInput): Promise<BoxBrainAvailabilityEntry>
```
**Important input fields**
- `spaceName: string`
- `mode: 'online' | 'do_not_disturb' | 'offline'`
- `reason?: string`
- `effectiveFrom: string`
- `until?: string`
- `sourceType?: 'schedule' | 'manual' | 'tool'`
- `eventId?: string`
- `metadata?: JsonValue | null`
### `listAvailabilityEntries(db, input)`
Lists explicit availability entries in chronological order.
Lists availability entries in chronological order.
```ts
listAvailabilityEntries(db: IdentityDB, input: ListAvailabilityEntriesInput): Promise<BoxBrainAvailabilityEntry[]>
@@ -184,32 +124,19 @@ Returns the active availability state at a timestamp plus the next transition.
getAvailabilitySnapshot(db: IdentityDB, input: GetAvailabilitySnapshotInput): Promise<BoxBrainAvailabilitySnapshot>
```
**Important types**
Important types:
```ts
type BoxBrainAvailabilityMode = 'online' | 'do_not_disturb' | 'offline';
type BoxBrainAvailabilitySourceType = 'default' | 'schedule' | 'manual' | 'tool';
interface BoxBrainAvailabilityEntry {
id: string;
mode: BoxBrainAvailabilityMode;
reason?: string;
effectiveFrom: string;
until?: string;
sourceType: BoxBrainAvailabilitySourceType;
eventId?: string;
createdAt?: string;
metadata?: JsonValue | null;
}
interface BoxBrainAvailabilitySnapshot {
current: BoxBrainAvailabilityEntry;
next: BoxBrainAvailabilityEntry | null;
}
```
## `conversation`
### `ConversationService`
Class-based entrypoint for inbound replies, proactive openings, and entry listing.
### `replyToConversation(db, input)`
Persists an inbound message and generates a DM-style response turn.
@@ -234,41 +161,29 @@ Lists stored conversation entries for a persona, optionally filtered by counterp
listConversationEntries(db: IdentityDB, input: ListConversationEntriesInput): Promise<BoxBrainConversationEntry[]>
```
**Required model roles for turn generation**
Both `replyToConversation` and `startConversation` require:
Required model roles for turn generation:
- `mandatoryMemoryModel: StructuredModelAdapter`
- `contextualMemoryModel: StructuredModelAdapter`
- `responseModel: StructuredModelAdapter`
**Turn result**
## `memory`
### `FactDraftMemoryStore`
Class-based entrypoint for persisting extracted fact drafts into IdentityDB.
### `persistFactDrafts(db, input)`
Low-level helper for writing BoxBrain fact drafts into IdentityDB.
```ts
interface ConversationTurnResult {
blocked: boolean;
blockedReason?: string;
blockedUntil?: string;
messages: BoxBrainMessage[];
usedMemories: BoxBrainMemoryReference[];
toolCallsExecuted: ConversationToolCall[];
}
```
**Message type**
```ts
interface BoxBrainMessage {
text: string;
typingDelaySeconds: number;
replyDelaySeconds: number;
totalDelaySeconds: number;
}
persistFactDrafts(db: IdentityDB, input: PersistFactDraftsInput): Promise<Fact[]>
```
## `timing`
### Availability constants
### Constants
```ts
ONLINE_AVAILABILITY
@@ -276,6 +191,10 @@ DND_AVAILABILITY
OFFLINE_AVAILABILITY
```
### `TimingProfile`
Class-based entrypoint for timing calculations.
### `createTypingDelay(message, options?)`
Computes a per-message typing delay based on message length.
@@ -346,6 +265,10 @@ interface SpecialDateProvider {
## `grok`
### `GrokApiClient`
Class-based xAI runtime client used by the Grok adapter helpers.
### `createGrokTextModelAdapter(options)`
Creates a `TextModelAdapter` that uses xAI chat completions.
@@ -370,21 +293,9 @@ createGrokAdapters(options: GrokAdapterBundleOptions): {
}
```
## `memory`
### `persistFactDrafts(db, input)`
Low-level helper for writing BoxBrain fact drafts into IdentityDB.
```ts
persistFactDrafts(db: IdentityDB, input: PersistFactDraftsInput): Promise<Fact[]>
```
Useful when extending BoxBrain with your own runtime modules.
## `types`
Selected important domain types:
Selected important domain types include:
```ts
type BoxBrainFactDomain =

@@ -1,48 +1,50 @@
# Getting Started
## Current setup assumptions
## Setup options
At the current stage, BoxBrain is a local TypeScript/Bun framework that depends on a sibling checkout of IdentityDB.
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.
The package configuration currently points to:
### Use BoxBrain in your own project
```json
"identitydb": "file:../IdentityDB"
```bash
bun add boxbrain identitydb
```
So a practical local setup looks like this:
### Work on the BoxBrain repository itself
```bash
git clone https://git.psw.kr/p-sw/BoxBrain.git
git clone https://git.psw.kr/p-sw/IdentityDB.git
```
with both repositories living next to each other.
## Install and verify
```bash
cd BoxBrain
bun install
```
## Verify the repository
```bash
bun run test
bun run check
bun run build
```
## Minimal dependencies
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:
```bash
rm -rf node_modules bun.lock
bun install
```
## Runtime requirements
You need:
- Node.js 20+
- Bun
- IdentityDB available at `../IdentityDB`
- an initialized IdentityDB database connection at runtime
## Create an IdentityDB instance
BoxBrain uses IdentityDB as the persistence layer.
A minimal in-memory setup:
```ts
import { IdentityDB } from 'identitydb';
@@ -58,7 +60,7 @@ For a persistent local database, replace `':memory:'` with a file path.
## Create model adapters
BoxBrain runtime code expects provider adapters instead of hard-coding a specific vendor inside the core functions.
BoxBrain expects provider adapters instead of hard-coding a vendor into the core runtime.
### Provider-agnostic contracts
@@ -84,7 +86,9 @@ const grok = createGrokAdapters({
## Initialize a persona
This is the first major BoxBrain workflow.
You can use either the functional helper or the class-based service entrypoint.
### Functional helper
```ts
import { IdentityDB } from 'identitydb';
@@ -109,18 +113,32 @@ const persona = await initializePersona(db, {
imageModel: grok.image,
generateProfileImage: true,
});
console.log(persona);
```
### What initialization does
### Class-based service
- generates a detailed biography
- can start from a single long freeform persona seed string
- extracts IdentityDB-ready facts
- creates or reuses an IdentityDB space (depending on options)
- optionally generates a profile image
- persists biography and image facts
```ts
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
@@ -151,14 +169,6 @@ console.log(schedule.events);
console.log(schedule.availabilityEntries);
```
### What schedule generation does
- reads persona facts and recent schedule continuity
- optionally loads external special-date context
- generates realistic events for a day/week/month window
- stores schedule facts in IdentityDB
- automatically emits schedule-derived availability entries
## Read or override availability
```ts
@@ -226,8 +236,6 @@ console.log(reply.toolCallsExecuted);
## Timing helpers
If you want to inspect or reuse BoxBrains timing rules directly:
```ts
import {
ONLINE_AVAILABILITY,
@@ -245,9 +253,9 @@ const typingDelay = createTypingDelay('지금 뭐해?', {
});
```
## Caveats
## Important caveats
### 1. BoxBrain is currently a library, not an HTTP service
### 1. BoxBrain is a library, not an HTTP service
You call the exported TypeScript APIs directly.
@@ -255,10 +263,10 @@ You call the exported TypeScript APIs directly.
The framework assumes IdentityDB is available and initialized.
### 3. Current local development uses a sibling checkout
### 3. Internal source folders are a repository concern
Because the package currently depends on `file:../IdentityDB`, a standalone `npm install boxbrain` style flow is not the current development path yet.
The repository is organized by domain folders under `src/`, but the published package is still consumed through the package root exports.
### 4. `better-sqlite3` native rebuilds may be needed in local dev
### 4. Clean Bun installs depend on trusted lifecycle scripts
If you hit a native module mismatch through the local IdentityDB dependency, rebuilding `better-sqlite3` from the IdentityDB workspace may be required.
The repository keeps `trustedDependencies` for `better-sqlite3` and `esbuild` so native/runtime build steps succeed during clean installs.

120
Home.md

@@ -2,85 +2,79 @@
BoxBrain is an IdentityDB-backed TypeScript framework for building **synthetic personas that feel like real DM contacts**.
It is not a finished chatbot product. It is a **framework for persona runtime design**: a reusable harness for turning long-term memory, schedules, availability, and multi-step LLM orchestration into believable human-like messaging behavior.
It is not a finished chatbot product. It is a **framework-first runtime library** for composing persona memory, schedules, availability, and multi-step LLM orchestration into believable messaging behavior.
## Why BoxBrain exists
Most LLM chat systems are optimized for correctness, utility, or task completion. BoxBrain exists for a different goal:
- make a persona feel like a **specific person**, not a generic assistant
- preserve continuity through **IdentityDB facts** instead of a short rolling prompt only
- let that persona have a **life outside the chat**
- make conversation timing and availability feel **human**, not instant and mechanical
- keep the whole system **framework-first**, so applications can compose their own transports and product UX on top
In short: BoxBrain is a harness for building personas that have memory, history, schedules, contactability, and DM-style messaging behavior.
## What the framework currently provides
## What BoxBrain currently provides
- provider-agnostic adapter contracts for text, structured-output, image, and special-date retrieval
- a ready-made xAI Grok adapter set for text, structured-output, and image generation
- one IdentityDB **space** per persona as the primary isolation boundary
- persona initialization from a long freeform seed string that the LLM breaks into biography-ready detail and IdentityDB facts
- LLM-generated biography creation followed by fact extraction into IdentityDB
- a ready-made xAI Grok adapter set
- one IdentityDB space per persona as the main isolation boundary
- persona initialization from a long freeform seed string, with optional supplemental structured hints
- biography generation and fact extraction into IdentityDB
- optional profile image generation during persona initialization
- realistic schedule generation for day, week, and month scopes
- schedule generation for `day`, `week`, and `month` scopes
- schedule persistence, listing, and pruning
- availability state persistence with schedule, manual, and tool-driven overrides
- availability snapshots with current and next transition resolution
- availability persistence with schedule/manual/tool overrides
- availability snapshots with current and next-transition resolution
- DM-style conversation orchestration for inbound replies and proactive openings
- two-stage memory retrieval for conversation turns: mandatory memories first, contextual memories second
- human-like first-reply delay and per-message typing delay helpers
- refusal / farewell flows that can trigger an availability-changing tool call
- human-like first-reply and typing delay helpers
## What is still planned
These are **not** implemented as part of the core library yet:
- HTTP/RPC wrappers around the runtime APIs
- additional ready-made vendor adapter packages beyond Grok
- production-focused runtime/persistence integrations beyond the in-process core library
- additional ready-made provider adapter packages beyond Grok
- production-focused runtime integrations beyond the in-process core library
## Recommended reading order
1. [Purpose and Architecture](Purpose-and-Architecture)
2. [Getting Started](Getting-Started)
3. [API Reference](API-Reference)
4. [xAI Grok Adapter](xAI-Grok-Adapter)
## BoxBrain mental model
A BoxBrain persona works like this:
1. **Initialize a persona** from a long freeform seed string, optionally with extra structured hints.
2. Generate a detailed biography.
3. Extract biography facts and store them in the persona's IdentityDB space.
4. Generate schedules anchored to time and external special dates.
5. Derive availability from schedule or explicit overrides.
6. Run conversations by retrieving mandatory/contextual memories, generating a turn plan, and emitting DM-style messages with delays.
## Current code surface
The package root currently exports:
- `./adapters`
- `./availability`
- `./conversation`
- `./grok`
- `./memory`
- `./persona`
- `./schedule`
- `./timing`
- `./types`
## Development status
Repository status at the time of writing:
## Current project status
- package/runtime: **Bun**
- language: **TypeScript**
- tests: **Vitest**
- build: **tsup**
- storage/memory dependency: **IdentityDB**
- persistence dependency: published **`identitydb@0.2.0`** from npm
- release automation: tag-triggered **Gitea Actions** workflow for npm publishing
See [Getting Started](Getting-Started) for a working local setup pattern.
## Recommended reading order
1. [Purpose and Architecture](Purpose-and-Architecture)
2. [Getting Started](Getting-Started)
3. [Source Layout](Source-Layout)
4. [API Reference](API-Reference)
5. [Release Workflow](Release-Workflow)
6. [xAI Grok Adapter](xAI-Grok-Adapter)
## Mental model
A BoxBrain persona works like this:
1. **Initialize a persona** from a long freeform seed string.
2. Generate a detailed biography.
3. Extract biography facts and store them in the persona's IdentityDB space.
4. Generate schedules anchored to time and optional special-date context.
5. Derive availability from schedule or explicit overrides.
6. Run conversations by retrieving memories, planning a turn, and emitting DM-style messages with delays.
## Public surface at a glance
The package root currently re-exports:
```ts
export * from './core/adapters';
export * from './core/types';
export * from './availability';
export * from './conversation';
export * from './memory';
export * from './persona';
export * from './providers/grok';
export * from './schedule';
export * from './timing';
```
That means consumers can use both:
- functional helpers like `initializePersona`, `generateSchedule`, and `replyToConversation`
- class-based entrypoints like `PersonaService`, `ScheduleService`, `AvailabilityService`, `ConversationService`, `FactDraftMemoryStore`, `TimingProfile`, and `GrokApiClient`
See [Getting Started](Getting-Started) for local setup and [API Reference](API-Reference) for the current exported runtime surface.

85
Release-Workflow.md Normal file

@@ -0,0 +1,85 @@
# Release Workflow
BoxBrain uses a **tag-triggered Gitea Actions workflow** to publish the package to npm.
## Trigger
Creating and pushing either of these tag forms starts the release workflow:
- `vX.Y.Z`
- `X.Y.Z`
The workflow file lives at:
```text
.gitea/workflows/npm-release.yml
```
## Verify stage
Before publishing, the workflow performs a full verification pass on the tagged source.
At a high level it:
1. installs release tooling in the CI container
2. clones the tagged BoxBrain source
3. verifies that the tag name matches `package.json` version
4. runs the full verify pipeline:
- `bun install --frozen-lockfile`
- `bun run test`
- `bun run check`
- `bun run build`
## Publish stage
Only after verify succeeds, the workflow:
1. clones the tagged source again
2. installs dependencies with Bun
3. builds the package
4. publishes to npm with `NODE_AUTH_TOKEN`
## IdentityDB dependency model
BoxBrain now depends on the published npm package:
```json
"identitydb": "0.2.0"
```
That means the release workflow **does not need a sibling IdentityDB checkout** anymore.
## Trusted Bun dependencies
The repository keeps these entries in `trustedDependencies`:
- `better-sqlite3`
- `esbuild`
This matters because clean Bun installs must allow the necessary lifecycle scripts for native/runtime build steps.
## Local verification before tagging
A normal local verification pass is:
```bash
bun run test
bun run check
bun run build
```
If you want to simulate a clean release-style clone:
```bash
rm -rf /tmp/boxbrain-release-check
git clone https://git.psw.kr/p-sw/BoxBrain.git /tmp/boxbrain-release-check
cd /tmp/boxbrain-release-check
bun install
bun run test && bun run check && bun run build
```
## Current published version
At the time of writing, BoxBrain has already been published as:
- `boxbrain@0.1.0`

52
Source-Layout.md Normal file

@@ -0,0 +1,52 @@
# Source Layout
The repository is now organized by **domain folders** under `src/` instead of a flat list of top-level runtime files.
## Current `src/` structure
- `src/core/` — shared adapter interfaces, common types, and fact helpers
- `src/persona/` — persona initialization runtime
- `src/schedule/` — schedule generation, listing, and pruning
- `src/availability/` — availability persistence and snapshot resolution
- `src/conversation/` — DM-style conversation orchestration
- `src/memory/` — fact-draft persistence into IdentityDB
- `src/timing/` — timing constants, delay helpers, and timing profile class
- `src/providers/grok/` — xAI-specific runtime client and adapter helpers
## Why the structure changed
The repository moved to domain folders to make the runtime easier to navigate as BoxBrain grew beyond a few flat modules.
This structure now makes it clearer where to look for:
- public entrypoints for a domain
- internal helpers related to that domain
- class-based service abstractions for stateful integrations
- provider-specific code that should stay separate from the provider-agnostic core
## Class-oriented runtime surface
The current repository also leans more heavily on class-based entrypoints while still keeping the existing functional helpers.
Notable exported classes:
- `PersonaService`
- `ScheduleService`
- `AvailabilityService`
- `ConversationService`
- `FactDraftMemoryStore`
- `TimingProfile`
- `GrokApiClient`
That means consumers can choose between:
- **functional usage** for quick direct calls
- **service-class usage** for cleaner composition inside larger applications
## Important packaging note
The folder layout described on this page is the **repository source structure**.
The published package is still consumed through the **package root exports**, not through documented public subpath exports.
See [API Reference](API-Reference) for the supported root-level public surface.

@@ -3,5 +3,7 @@
- [Home](Home)
- [Purpose and Architecture](Purpose-and-Architecture)
- [Getting Started](Getting-Started)
- [Source Layout](Source-Layout)
- [API Reference](API-Reference)
- [Release Workflow](Release-Workflow)
- [xAI Grok Adapter](xAI-Grok-Adapter)