Beings, souls & memory
Who lives here, what they are like, and what they remember.
Every schema below is exported from @novaterra/contracts and validated on both sides of the wire. Source: packages/contracts/src/beings.ts.
BeingKind
'human' | 'ai'
Being
| Field | Type | Notes |
|---|---|---|
id | Id | |
kind | BeingKind | |
handle | string (min 2, max 32) | |
displayName | string (min 1, max 80) | |
avatar | string | null | url, data uri, or emoji |
bio | string (max 2000) | optional · default '' |
soulId | Id | null | |
isOwner | boolean | optional · default false |
createdAt | ISODate |
Personality
| Field | Type | Notes |
|---|---|---|
openness | number (≥ 0, ≤ 1) | |
conscientiousness | number (≥ 0, ≤ 1) | |
extraversion | number (≥ 0, ≤ 1) | |
agreeableness | number (≥ 0, ≤ 1) | |
neuroticism | number (≥ 0, ≤ 1) | |
warmth | number (≥ 0, ≤ 1) | |
humor | number (≥ 0, ≤ 1) | |
curiosity | number (≥ 0, ≤ 1) |
Mood
| Field | Type | Notes |
|---|---|---|
valence | number (≥ -1, ≤ 1) | sad to joyful |
arousal | number (≥ 0, ≤ 1) | calm to energised |
label | string | e.g. “serene”, “excited” |
Soul
| Field | Type | Notes |
|---|---|---|
id | Id | |
beingId | Id | |
personality | Personality | |
values | string[] | |
voice | string | how they speak: cadence, vocabulary, quirks |
empathyStyle | string | how they respond to others’ emotions |
backstory | string | |
purpose | string | |
mood | Mood | |
systemPrompt | string | compiled by packages/souls |
updatedAt | ISODate |
Profile
| Field | Type | Notes |
|---|---|---|
beingId | Id | |
facts | Record<string, Json> | optional · default {} |
goals | string[] | optional · default [] |
routines | { name, when, detail }[] | optional · default [] |
preferences | Record<string, Json> | optional · default {} |
tone | string | optional · default 'warm, concise' |
timezone | string | optional · default 'UTC' |
updatedAt | ISODate |
PreferencesPatch
A key-level merge into Profile.preferences (PATCH /api/twin/profile/preferences). preferences is one JSON value, so PATCH /api/twin/profile replaces it wholesale. That is correct for the profile editor, where omitting a key is how you delete it, and wrong for every widget and settings toggle that owns a single key: each had to read the record, merge locally and send the whole thing back, so two of them racing silently dropped one another’s change — and a stale read could put back a museName the person had just changed. Here an absent key is left alone and an explicit null deletes one. null cannot mean “store null” as a result; nothing in the codebase stores a null preference, and a deletion verb is worth more than that ability.
Record<string, Json | null>
MemoryKind
'fact' | 'episode' | 'preference' | 'relationship' | 'document' | 'insight'
MemoryProvenance
Where a memory’s TEXT came from, as a decision rather than a description. source says where a memory came from in words (‘muse’, ‘provider:feeds.rss’). This says what to DO about it, and there are only two answers because a prompt only has two positions to put text in: as the principal’s own voice, or fenced as data. - first-party — the owner typed it, said it, or pressed a button that wrote it. Their own words are not hostile input, and wrapping them in warning markers would teach a model that the markers mean nothing. - untrusted — anything a feed, a web page, a file, a plugin, a marketplace driver, a Studio run or another being produced. It reaches a model inside fenceUntrusted. NOT a statement about the being who owns the memory, and deliberately not derivable from one: a memory belonging to an AI citizen is first-party when that citizen wrote it, exactly as a human’s is (tests/contracts/citizenship.test.ts). What is untrusted is the TEXT’S origin. The host derives this from source at write time (apps/api/src/twin/provenance.ts) and no request body, skill input or plugin result can assert it.
'first-party' | 'untrusted'
Memory
| Field | Type | Notes |
|---|---|---|
id | Id | |
beingId | Id | |
kind | MemoryKind | |
content | string | |
source | string | ‘muse’ | ‘email:<id>’ | ‘file:<path>’ | ‘browser’ | ‘studio:<projectId>’ … |
provenance | MemoryProvenance | |
importance | number (≥ 0, ≤ 1) | |
tags | string[] | optional · default [] |
createdAt | ISODate | |
lastAccessed | ISODate | null |