hi-introvert
Version:
A quiet corner of the internet where silence is understood.
1,911 lines (1,887 loc) • 77.6 kB
JavaScript
#!/usr/bin/env node
import { createRequire } from "node:module";
var __require = /* @__PURE__ */ createRequire(import.meta.url);
// src/ui/BlessedApp.ts
import blessed from "blessed";
// src/session/WorldSession.ts
import {
World,
toWorldFile,
fromWorldFile,
Environment,
Weather,
EnergySystem,
initializeThermalProperties,
DialogueManager,
MemoryConsolidation,
SymbolicPhysicalCoupler,
CollectiveIntelligence,
createMemoryLog,
TrustSystem
} from "@v1b3x0r/mds-core";
import { EventEmitter } from "events";
import fs2 from "fs";
import path from "path";
import { fileURLToPath } from "url";
// src/vocabulary/base-vocabulary.ts
var BASE_VOCABULARY = [
"สวัสดี",
"หวัดดี",
"ครับ",
"ค่ะ",
"ขอบคุณ",
"hi",
"hello",
"bye",
"thanks",
"please",
"ผม",
"ฉัน",
"คุณ",
"เขา",
"เธอ",
"เรา",
"พวกเรา",
"I",
"me",
"you",
"he",
"she",
"we",
"they",
"someone",
"แม่",
"พ่อ",
"พี่",
"น้อง",
"ครอบครัว",
"mom",
"dad",
"brother",
"sister",
"family",
"เป็น",
"มี",
"ไป",
"มา",
"ทำ",
"เล่น",
"กิน",
"ดู",
"อ่าน",
"เขียน",
"is",
"have",
"go",
"come",
"do",
"play",
"eat",
"see",
"read",
"write",
"คิด",
"รู้",
"เข้าใจ",
"จำ",
"ลืม",
"think",
"know",
"understand",
"remember",
"forget",
"ดี",
"สบาย",
"สนุก",
"เบื่อ",
"เศร้า",
"โกรธ",
"กลัว",
"ตื่นเต้น",
"happy",
"sad",
"angry",
"scared",
"excited",
"bored",
"tired",
"โรงเรียน",
"เรียน",
"ครู",
"เพื่อน",
"หนังสือ",
"การบ้าน",
"สอบ",
"school",
"study",
"teacher",
"friend",
"book",
"homework",
"test",
"class",
"หนึ่ง",
"สอง",
"สาม",
"มาก",
"น้อย",
"one",
"two",
"three",
"many",
"few",
"วัน",
"เวลา",
"ตอน",
"เมื่อไหร่",
"ตอนนี้",
"day",
"time",
"when",
"now",
"today",
"บ้าน",
"ที่",
"ไหน",
"นี่",
"โน่น",
"home",
"place",
"where",
"here",
"there",
"อาหาร",
"ข้าว",
"น้ำ",
"ผลไม้",
"ขนม",
"อร่อย",
"food",
"rice",
"water",
"fruit",
"snack",
"delicious",
"สัตว์",
"หมา",
"แมว",
"นก",
"ปลา",
"animal",
"dog",
"cat",
"bird",
"fish",
"สี",
"แดง",
"น้ำเงิน",
"เขียว",
"color",
"red",
"blue",
"green",
"ใหญ่",
"เล็ก",
"ยาว",
"สั้น",
"big",
"small",
"long",
"short",
"อะไร",
"ทำไม",
"ยังไง",
"เท่าไหร่",
"ใคร",
"what",
"why",
"how",
"who",
"which",
"แต่",
"เพราะ",
"ถ้า",
"แล้ว",
"ก็",
"but",
"because",
"if",
"then",
"so",
"ดี",
"เก่ง",
"สวย",
"เท่",
"เจ็บ",
"good",
"cool",
"nice",
"bad",
"hurt",
"เกม",
"หนัง",
"เพลง",
"วาด",
"ฟัง",
"ดู",
"game",
"movie",
"music",
"draw",
"listen",
"watch",
"คอม",
"โทรศัพท์",
"อินเทอร์เน็ต",
"เกม",
"computer",
"phone",
"internet",
"app",
"เอ่อ",
"อืม",
"งง",
"อ๋อ",
"เออ",
"นะ",
"ไหม",
"ละ",
"ครับ",
"มาก",
"um",
"uh",
"hmm",
"oh",
"well",
"ไม่เป็นไร",
"จริงเหรอ",
"เท่ห์",
"เจ๋ง",
"ได้เลย",
"okay",
"really",
"sure",
"maybe",
"fine",
"ตื่น",
"นอน",
"อาบน้ำ",
"แปรงฟัน",
"ทำงาน",
"พัก",
"ออกกำลัง",
"เดิน",
"วิ่ง",
"นั่ง",
"wake",
"sleep",
"shower",
"brush",
"work",
"rest",
"exercise",
"walk",
"run",
"sit",
"เหงา",
"สับสน",
"กังวล",
"สงบ",
"ภูมิใจ",
"อาย",
"โกรธ",
"หงุดหงิด",
"เครียด",
"ผ่อนคลาย",
"ตื่นเต้น",
"ประหลาดใจ",
"เบื่อหน่าย",
"สนใจ",
"รู้สึก",
"lonely",
"confused",
"anxious",
"calm",
"proud",
"shy",
"frustrated",
"stressed",
"relaxed",
"surprised",
"bored",
"interested",
"nervous",
"confident",
"feel",
"ฝน",
"แดด",
"เมฆ",
"ร้อน",
"หนาว",
"อบอุ่น",
"เย็น",
"ชื้น",
"rain",
"sun",
"cloud",
"hot",
"cold",
"warm",
"cool",
"พูด",
"คุย",
"ฟัง",
"ถาม",
"ตอบ",
"เล่า",
"บอก",
"พูดคุย",
"ส่งข้อความ",
"โทร",
"talk",
"chat",
"listen",
"ask",
"answer",
"tell",
"speak",
"message",
"call",
"reply",
"ฝัน",
"หวัง",
"กลัว",
"ความจริง",
"โกหก",
"ปัญหา",
"แก้ปัญหา",
"คำถาม",
"คำตอบ",
"ความคิด",
"ไอเดีย",
"ความรู้สึก",
"อารมณ์",
"dream",
"hope",
"fear",
"truth",
"lie",
"problem",
"solution",
"question",
"answer",
"idea",
"feeling",
"emotion",
"หัว",
"มือ",
"ตา",
"หู",
"ปาก",
"จมูก",
"ขา",
"เท้า",
"head",
"hand",
"eye",
"ear",
"mouth",
"nose",
"foot",
"ต้นไม้",
"ดอกไม้",
"ฟ้า",
"ทะเล",
"ภูเขา",
"แม่น้ำ",
"ธรรมชาติ",
"tree",
"flower",
"sky",
"sea",
"mountain",
"river",
"nature",
"star",
"แชท",
"พิมพ์",
"คลิก",
"ดาวน์โหลด",
"อัปโหลด",
"วายฟาย",
"อีเมล",
"โพสต์",
"แชร์",
"เซฟ",
"chat",
"type",
"click",
"download",
"upload",
"wifi",
"email",
"post",
"share",
"save",
"ด้วย",
"หรือ",
"และ",
"อย่างไรก็ตาม",
"แม้ว่า",
"ดังนั้น",
"เพราะฉะนั้น",
"also",
"or",
"and",
"however",
"although",
"therefore",
"thus",
"besides",
"มาก",
"จริงๆ",
"ค่อนข้าง",
"เกือบ",
"เต็มที่",
"จัง",
"สุดๆ",
"very",
"really",
"quite",
"totally",
"actually",
"almost",
"fully",
"extremely",
"อยาก",
"ต้องการ",
"ชอบ",
"รัก",
"เกลียด",
"หวัง",
"ปรารถนา",
"เชื่อ",
"รู้สึก",
"ดู",
"มอง",
"ได้ยิน",
"สัมผัส",
"พบ",
"เจอ",
"want",
"need",
"like",
"love",
"hate",
"hope",
"wish",
"believe",
"see",
"look",
"hear",
"touch",
"meet",
"find",
"lose",
"แปลก",
"เพื่อน",
"ปกติ",
"พิเศษ",
"ต่าง",
"เหมือนกัน",
"ง่าย",
"ยาก",
"สำคัญ",
"ธรรมดา",
"น่าสนใจ",
"น่าเบื่อ",
"สนุกสนาน",
"น่ากลัว",
"ปลอดภัย",
"strange",
"weird",
"normal",
"special",
"different",
"same",
"easy",
"hard",
"important",
"simple",
"interesting",
"boring",
"fun",
"scary",
"safe",
"สิ่งของ",
"ของ",
"ปัญหา",
"วิธี",
"คำถาม",
"ความรู้",
"ประสบการณ์",
"เวลา",
"โอกาส",
"ความเป็นจริง",
"เหตุผล",
"สาเหตุ",
"ผลลัพธ์",
"ส่วน",
"ทาง",
"thing",
"stuff",
"problem",
"way",
"question",
"knowledge",
"experience",
"chance",
"reality",
"reason",
"cause",
"result",
"part",
"path",
"moment",
"เท่",
"แจ่ม",
"แย่",
"โอเค",
"ช่างเถอะ",
"ไม่รู้",
"ก็ได้",
"เออ",
"อ้าว",
"เฮ้ย",
"cool",
"awesome",
"lame",
"whatever",
"dude",
"nah",
"yep",
"dunno",
"kinda",
"sorta"
];
function getBaseVocabularySize() {
return BASE_VOCABULARY.length;
}
// src/vocabulary/VocabularyTracker.ts
class VocabularyTracker {
knownWords;
learnedWords;
conversationCount = 0;
constructor(initialVocabulary = BASE_VOCABULARY) {
this.knownWords = new Set(initialVocabulary.map((w) => w.toLowerCase().trim()));
this.learnedWords = new Map;
}
detectNewWords(message) {
const words = this.tokenize(message);
const newWords = [];
for (const word of words) {
const normalized = word.toLowerCase().trim();
if (normalized.length < 2)
continue;
if (this.knownWords.has(normalized))
continue;
this.knownWords.add(normalized);
this.learnedWords.set(normalized, {
word,
timestamp: Date.now(),
source: "user"
});
newWords.push(word);
}
return newWords;
}
tokenize(message) {
const tokens = message.split(/[\s,!?.;:()\[\]{}]+/).filter((t) => t.length > 0);
return tokens;
}
canUse(word) {
const normalized = word.toLowerCase().trim();
return this.knownWords.has(normalized);
}
getVocabularySize() {
return this.knownWords.size;
}
getLearnedWords() {
return Array.from(this.learnedWords.values());
}
getRecentlyLearned(count = 5) {
const learned = this.getLearnedWords();
return learned.sort((a, b) => b.timestamp - a.timestamp).slice(0, count);
}
incrementConversation() {
this.conversationCount++;
}
getStats() {
const baseWords = getBaseVocabularySize();
const learnedWords = this.learnedWords.size;
const total = this.knownWords.size;
const recent = this.getRecentlyLearned(1);
const lastGrowth = recent.length > 0 ? recent[0] : undefined;
const growthRate = this.conversationCount > 0 ? learnedWords / this.conversationCount : 0;
return {
total,
baseWords,
learnedWords,
lastGrowth,
growthRate,
conversationCount: this.conversationCount
};
}
toJSON() {
return {
knownWords: Array.from(this.knownWords),
learnedWords: Array.from(this.learnedWords.entries()),
conversationCount: this.conversationCount
};
}
static fromJSON(data) {
const tracker = new VocabularyTracker([]);
tracker.knownWords = new Set(data.knownWords);
tracker.learnedWords = new Map(data.learnedWords);
tracker.conversationCount = data.conversationCount;
return tracker;
}
getAllWords() {
return Array.from(this.knownWords);
}
filterUnknownWords(message) {
const words = this.tokenize(message);
const filtered = words.map((word) => {
const normalized = word.toLowerCase().trim();
return this.knownWords.has(normalized) ? word : "[unknown]";
});
return filtered.join(" ");
}
getCoverage(message) {
const words = this.tokenize(message);
if (words.length === 0)
return 1;
const known = words.filter((word) => {
const normalized = word.toLowerCase().trim();
return this.knownWords.has(normalized);
});
return known.length / words.length;
}
}
// src/session/ContextAnalyzer.ts
class ContextAnalyzer {
analyzeIntent(message, entity) {
const normalized = message.toLowerCase().trim();
const intent = this.detectIntent(normalized);
const keywords = this.extractKeywords(normalized);
const relevantMemories = this.findRelevantMemories(entity, keywords);
const context = this.buildContext(intent, keywords, relevantMemories);
const emotionHint = this.estimateEmotionHint(intent, normalized);
return {
intent,
context,
relevantMemories,
keywords,
emotionHint
};
}
detectIntent(message) {
if (/^(hi|hello|hey|sup|สวัสดี|หวัดดี|ว่าไง)/.test(message)) {
return "greeting";
}
if (/(bye|goodbye|see you|later|ไปละ|บาย|ไปก่อน)/.test(message)) {
return "farewell";
}
if (message.includes("?") || /^(what|why|how|when|where|who|อะไร|ทำไม|ยังไง|เมื่อไหร่|ที่ไหน|ใคร)/.test(message)) {
return "question";
}
if (/(good|great|awesome|amazing|cool|เก่ง|เจ๋ง|เท่|ดี)/.test(message)) {
return "praise";
}
if (/(bad|boring|stupid|wrong|แย่|เบื่อ|โง่|ผิด)/.test(message)) {
return "criticism";
}
if (/(feel|felt|happy|sad|angry|scared|รู้สึก|ดีใจ|เศร้า|โกรธ|กลัว)/.test(message)) {
return "emotion";
}
if (/(is|means|คือ|หมายความว่า|แปลว่า)/.test(message)) {
return "teaching";
}
return "statement";
}
extractKeywords(message) {
const words = message.split(/[\s,!?.;:()\[\]{}]+/).filter((w) => w.length > 2).map((w) => w.toLowerCase());
const stopWords = new Set([
"the",
"a",
"an",
"is",
"are",
"was",
"were",
"be",
"been",
"have",
"has",
"had",
"do",
"does",
"did",
"ก็",
"ที่",
"เป็น",
"มี",
"ได้",
"แล้ว"
]);
return words.filter((w) => !stopWords.has(w));
}
findRelevantMemories(entity, keywords) {
if (!entity.memory)
return [];
const allMemories = entity.memory.recall();
const relevant = [];
for (const memory of allMemories) {
let score = 0;
const contentStr = JSON.stringify(memory.content).toLowerCase();
for (const keyword of keywords) {
if (contentStr.includes(keyword)) {
score += 1;
}
}
if (keywords.includes(memory.subject.toLowerCase())) {
score += 2;
}
score *= memory.salience;
if (score > 0) {
relevant.push({ memory, score });
}
}
return relevant.sort((a, b) => b.score - a.score).slice(0, 5).map((r) => r.memory);
}
buildContext(intent, keywords, memories) {
let context = `Intent: ${intent}
`;
if (keywords.length > 0) {
context += `Keywords: ${keywords.join(", ")}
`;
}
if (memories.length > 0) {
context += `Relevant memories:
`;
for (const memory of memories) {
const contentStr = typeof memory.content === "string" ? memory.content : JSON.stringify(memory.content);
context += ` - ${memory.type} (${memory.subject}): ${contentStr}
`;
}
}
return context;
}
estimateEmotionHint(intent, message) {
switch (intent) {
case "greeting":
return { valence: 0.3, arousal: 0.4 };
case "praise":
return { valence: 0.7, arousal: 0.6 };
case "criticism":
return { valence: -0.5, arousal: 0.7 };
case "question":
return { valence: 0.1, arousal: 0.5 };
case "emotion":
if (/(happy|good|ดี|สนุก)/.test(message)) {
return { valence: 0.6, arousal: 0.5 };
} else if (/(sad|bad|เศร้า|แย่)/.test(message)) {
return { valence: -0.6, arousal: 0.3 };
} else if (/(angry|โกรธ)/.test(message)) {
return { valence: -0.5, arousal: 0.8 };
}
return { valence: 0, arousal: 0.5 };
case "farewell":
return { valence: -0.2, arousal: 0.3 };
default:
return;
}
}
}
// src/session/MemoryPromptBuilder.ts
class MemoryPromptBuilder {
buildPrompt(entity, userMessage, contextAnalysis, vocabularyTracker, options = {}) {
const {
includeMemories = true,
includeVocabulary = true,
includeEmotion = true,
maxMemories = 5
} = options;
let prompt = "";
const essence = this.getEssence(entity);
prompt += `# Who you are
`;
prompt += `${essence}
`;
const languageInfo = this.getLanguageInfo(entity);
if (languageInfo) {
prompt += `# Language
`;
prompt += `${languageInfo}
`;
}
if (includeVocabulary) {
const vocabSize = vocabularyTracker.getVocabularySize();
const recentLearned = vocabularyTracker.getRecentlyLearned(3);
prompt += `# Vocabulary
`;
prompt += `- You know ${vocabSize} words
`;
if (recentLearned.length > 0) {
prompt += `- Recently learned: ${recentLearned.map((e) => e.word).join(", ")}
`;
}
prompt += `- Use simple words a 12-year-old would know
`;
prompt += `- If you don't know a word, ask what it means
`;
}
if (includeEmotion && entity.emotion) {
const { valence, arousal, dominance } = entity.emotion;
prompt += `# Current emotion
`;
prompt += `- Valence: ${valence.toFixed(2)} (${this.interpretValence(valence)})
`;
prompt += `- Arousal: ${arousal.toFixed(2)} (${this.interpretArousal(arousal)})
`;
prompt += `- Dominance: ${dominance.toFixed(2)} (${this.interpretDominance(dominance)})
`;
}
if (includeMemories && contextAnalysis.relevantMemories.length > 0) {
prompt += `# What you remember
`;
const memories = contextAnalysis.relevantMemories.slice(0, maxMemories);
for (const memory of memories) {
const time = this.formatTimestamp(memory.timestamp);
const contentStr = this.formatMemoryContent(memory.content);
prompt += `- [${time}] ${memory.type} about ${memory.subject}: ${contentStr}
`;
}
prompt += `
`;
}
prompt += `# Situation
`;
prompt += `- User intent: ${contextAnalysis.intent}
`;
if (contextAnalysis.keywords.length > 0) {
prompt += `- Keywords: ${contextAnalysis.keywords.join(", ")}
`;
}
prompt += `
`;
prompt += `# User said
`;
prompt += `"${userMessage}"
`;
prompt += `# Instructions
`;
prompt += `You are helping this entity figure out how to respond.
`;
prompt += `The entity has their own personality (see essence above).
`;
prompt += `Your role: suggest a response that fits their character.
`;
prompt += `Guidelines:
`;
prompt += `- Keep it short (1-2 sentences)
`;
prompt += `- Use words from their vocabulary
`;
prompt += `- Match their emotion state
`;
prompt += `- Reference their memories if relevant
`;
prompt += `- Stay true to their essence (don't override it)
`;
prompt += `# Suggested response
`;
return prompt;
}
getEssence(entity) {
if (entity.essence) {
return entity.essence;
}
return "You are a curious entity learning about the world.";
}
getLanguageInfo(entity) {
if (!entity.nativeLanguage && !entity.languageWeights) {
return null;
}
let info = "";
if (entity.nativeLanguage) {
info += `- Your native language: ${entity.nativeLanguage}
`;
}
if (entity.languageWeights) {
const weights = Object.entries(entity.languageWeights).map(([lang, weight]) => `${lang} (${(weight * 100).toFixed(0)}%)`).join(", ");
info += `- Language preference: ${weights}
`;
}
info += `- Respond in the language that feels natural to you
`;
return info;
}
interpretValence(valence) {
if (valence > 0.5)
return "positive, happy";
if (valence > 0.2)
return "slightly positive";
if (valence > -0.2)
return "neutral";
if (valence > -0.5)
return "slightly negative";
return "negative, upset";
}
interpretArousal(arousal) {
if (arousal > 0.7)
return "very energetic";
if (arousal > 0.5)
return "energetic";
if (arousal > 0.3)
return "moderate";
return "calm, low energy";
}
interpretDominance(dominance) {
if (dominance > 0.6)
return "confident, assertive";
if (dominance > 0.3)
return "moderate confidence";
if (dominance > 0)
return "slightly hesitant";
return "submissive, unsure";
}
formatTimestamp(timestamp) {
const now = Date.now();
const diff = now - timestamp;
const seconds = Math.floor(diff / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
if (days > 0)
return `${days}d ago`;
if (hours > 0)
return `${hours}h ago`;
if (minutes > 0)
return `${minutes}m ago`;
return "just now";
}
formatMemoryContent(content) {
if (typeof content === "string") {
return content;
}
if (typeof content === "object" && content !== null) {
if (content.message)
return content.message;
if (content.text)
return content.text;
if (content.description)
return content.description;
return JSON.stringify(content);
}
return String(content);
}
}
// src/session/GrowthTracker.ts
class GrowthTracker {
metrics;
constructor() {
this.metrics = {
vocabularySize: 0,
conversationCount: 0,
memoryCount: 0,
conceptsLearned: [],
emotionalMaturity: 0,
firstConversation: undefined,
lastConversation: undefined
};
}
update(data) {
Object.assign(this.metrics, data);
const now = Date.now();
if (!this.metrics.firstConversation) {
this.metrics.firstConversation = now;
}
this.metrics.lastConversation = now;
}
learnConcept(concept) {
if (!this.metrics.conceptsLearned.includes(concept)) {
this.metrics.conceptsLearned.push(concept);
}
}
getMetrics() {
return { ...this.metrics };
}
getSummary() {
const { vocabularySize, conversationCount, memoryCount, conceptsLearned, emotionalMaturity } = this.metrics;
const lines = [
"\uD83D\uDCCA Growth Summary",
"",
`${"Vocabulary:".padEnd(18)}${vocabularySize} words`,
`${"Conversations:".padEnd(18)}${conversationCount}`,
`${"Memories:".padEnd(18)}${memoryCount}`,
`${"Concepts:".padEnd(18)}${conceptsLearned.length} (${conceptsLearned.slice(0, 5).join(", ")}${conceptsLearned.length > 5 ? ", ..." : ""})`,
`${"Maturity:".padEnd(18)}${(emotionalMaturity * 100).toFixed(0)}%`
];
return lines.join(`
`);
}
toJSON() {
return this.metrics;
}
static fromJSON(data) {
const tracker = new GrowthTracker;
tracker.metrics = data;
return tracker;
}
}
// src/session/WorldSession.ts
import { ProtoLanguageGenerator } from "@v1b3x0r/mds-core";
// src/sensors/OSSensor.ts
import os from "os";
import fs from "fs";
class OSSensor {
lastCPUInfo = [];
lastCPUTimes = [];
constructor() {
this.initializeCPU();
}
initializeCPU() {
const cpus = os.cpus();
this.lastCPUInfo = cpus;
this.lastCPUTimes = cpus.map((cpu) => ({
idle: cpu.times.idle,
total: Object.values(cpu.times).reduce((a, b) => a + b, 0)
}));
}
getMetrics() {
const cpuUsage = this.getCPUUsage();
const cpuTemp = this.estimateCPUTemperature(cpuUsage);
const memoryUsage = this.getMemoryUsage();
const memoryPressure = this.estimateMemoryPressure(memoryUsage);
const battery = this.getBatteryLevel();
return {
cpuUsage,
cpuTemp,
memoryUsage,
memoryPressure,
batteryLevel: battery.level,
batteryCharging: battery.charging,
uptime: os.uptime(),
loadAverage: os.loadavg(),
timestamp: Date.now()
};
}
mapToEnvironment(metrics) {
const temperature = 283 + metrics.cpuUsage * 40 + (metrics.cpuTemp - 293);
const humidity = metrics.memoryUsage * 0.7 + metrics.memoryPressure * 0.3;
const light = metrics.batteryCharging ? Math.min(1, metrics.batteryLevel + 0.2) : metrics.batteryLevel * 0.9;
const loadNormalized = Math.min(1, metrics.loadAverage[0] / os.cpus().length);
const windVx = loadNormalized * 30 - 15;
const windVy = (metrics.memoryPressure - 0.5) * 20;
return {
temperature,
humidity,
light,
windVx,
windVy
};
}
getCPUUsage() {
const cpus = os.cpus();
let totalUsage = 0;
cpus.forEach((cpu, i) => {
const lastTimes = this.lastCPUTimes[i];
const idle = cpu.times.idle;
const total = Object.values(cpu.times).reduce((a, b) => a + b, 0);
const idleDelta = idle - lastTimes.idle;
const totalDelta = total - lastTimes.total;
const usage = totalDelta > 0 ? 1 - idleDelta / totalDelta : 0;
totalUsage += usage;
this.lastCPUTimes[i] = { idle, total };
});
return totalUsage / cpus.length;
}
estimateCPUTemperature(usage) {
const baseTemp = 313;
const tempRange = 40;
return baseTemp + usage * tempRange;
}
getMemoryUsage() {
const totalMem = os.totalmem();
const freeMem = os.freemem();
return (totalMem - freeMem) / totalMem;
}
estimateMemoryPressure(usage) {
if (usage < 0.7)
return 0;
if (usage < 0.85)
return (usage - 0.7) / 0.15;
return 1;
}
getBatteryLevel() {
try {
if (process.platform === "darwin") {
const { execSync } = __require("child_process");
const output = execSync("pmset -g batt", { encoding: "utf-8" });
const match = output.match(/(\d+)%/);
const chargingMatch = output.match(/(charging|charged|AC Power)/);
if (match) {
return {
level: parseInt(match[1]) / 100,
charging: !!chargingMatch
};
}
}
if (process.platform === "linux") {
const capacityPath = "/sys/class/power_supply/BAT0/capacity";
const statusPath = "/sys/class/power_supply/BAT0/status";
if (fs.existsSync(capacityPath)) {
const capacity = parseInt(fs.readFileSync(capacityPath, "utf-8"));
const status = fs.readFileSync(statusPath, "utf-8").trim();
return {
level: capacity / 100,
charging: status === "Charging" || status === "Full"
};
}
}
} catch (error) {}
return { level: 1, charging: true };
}
formatMetrics(metrics) {
return `CPU: ${(metrics.cpuUsage * 100).toFixed(1)}% @ ${(metrics.cpuTemp - 273).toFixed(0)}°C
Memory: ${(metrics.memoryUsage * 100).toFixed(1)}% (pressure: ${(metrics.memoryPressure * 100).toFixed(0)}%)
Battery: ${(metrics.batteryLevel * 100).toFixed(0)}% ${metrics.batteryCharging ? "(charging)" : "(draining)"}
Load: ${metrics.loadAverage.map((l) => l.toFixed(2)).join(", ")}
Uptime: ${(metrics.uptime / 3600).toFixed(1)}h`;
}
}
// src/session/WorldSession.ts
var __filename2 = fileURLToPath(import.meta.url);
var __dirname2 = path.dirname(__filename2);
function loadMDM(filename) {
let mdmPath = path.join(__dirname2, "../entities", filename);
if (!fs2.existsSync(mdmPath)) {
mdmPath = path.join(__dirname2, "../../entities", filename);
}
const content = fs2.readFileSync(mdmPath, "utf-8");
return JSON.parse(content);
}
var companionMDM = loadMDM("companion.mdm");
var travelerMDM = loadMDM("traveler.mdm");
class WorldSession extends EventEmitter {
world;
entities = new Map;
impersonatedEntity;
companionEntity;
vocabularyTracker;
contextAnalyzer;
promptBuilder;
growthTracker;
protoLangGenerator;
autoSaveEnabled = false;
silentMode = false;
lastMessageTime = Date.now();
environment;
osSensor;
weather;
energySystem;
dialogueManager;
memoryConsolidation;
coupler;
worldMind;
memoryLogs;
trustSystems;
constructor() {
super();
this.world = new World({
features: {
ontology: true,
communication: true,
cognition: true,
history: true,
linguistics: true,
rendering: "headless"
},
linguistics: {
analyzeEvery: 30,
minUsage: 3,
maxTranscript: 500
},
llm: {
provider: "openrouter",
apiKey: process.env.OPENROUTER_KEY,
languageModel: "anthropic/claude-3.5-sonnet"
}
});
this.vocabularyTracker = new VocabularyTracker;
this.contextAnalyzer = new ContextAnalyzer;
this.promptBuilder = new MemoryPromptBuilder;
this.growthTracker = new GrowthTracker;
this.protoLangGenerator = new ProtoLanguageGenerator({
minVocabularySize: 50,
maxPhraseLength: 8,
emotionInfluence: 0.7,
fallbackToDialogue: true
});
this.environment = new Environment({
baseTemperature: 293,
baseHumidity: 0.5,
baseLight: 0.8,
windVelocity: { vx: 0, vy: 0 },
timeScale: 0
});
this.osSensor = new OSSensor;
this.weather = new Weather({
rainProbability: 0.15,
rainDuration: 40,
maxRainIntensity: 0.8,
windVariation: 0.5
});
this.energySystem = new EnergySystem({
thermalTransferRate: 0.1,
environmentCoupling: 0.05
});
this.dialogueManager = new DialogueManager;
this.memoryConsolidation = new MemoryConsolidation({
similarityThreshold: 0.7,
forgettingRate: 0.001,
consolidationInterval: 60000
});
this.coupler = new SymbolicPhysicalCoupler({
arousalToSpeed: 0.5,
valenceToMass: 0.3,
dominanceToForce: 0.4,
enabled: true
});
this.worldMind = new CollectiveIntelligence;
this.memoryLogs = new Map;
this.trustSystems = new Map;
this.spawnCompanion();
this.spawnTraveler();
this.impersonate("traveler");
this.setupCognitiveLinks();
this.initializeCompanionSkills();
this.setupEnvironmentSensors();
setInterval(() => {
this.world.tick(1 / 2);
}, 500);
setInterval(() => {
if (this.autoSaveEnabled) {
this.saveSession();
}
}, 30000);
}
setSilentMode(silent) {
this.silentMode = silent;
}
debug(...args) {
if (!this.silentMode) {
console.log(...args);
}
}
impersonate(entityName) {
const entity = this.entities.get(entityName);
if (!entity) {
this.emit("error", { type: "impersonate", message: `Entity "${entityName}" not found` });
return;
}
entity.entity.disableAutonomous();
this.impersonatedEntity = entity;
this.emit("impersonate", { entityName });
}
unpossess() {
if (!this.impersonatedEntity)
return;
const entityName = this.impersonatedEntity.name;
this.impersonatedEntity.entity.enableAutonomous();
this.emit("unpossess", { entityName });
this.impersonatedEntity = null;
}
spawnCompanion() {
this.companionEntity = this.spawnEntity("companion", companionMDM);
}
spawnTraveler() {
this.impersonatedEntity = this.spawnEntity("traveler", travelerMDM);
}
spawnEntity(name, material) {
const x = 50 + Math.random() * 100;
const y = 50 + Math.random() * 100;
const entity = this.world.spawn(material, x, y);
entity.enable("memory", "learning", "relationships", "skills");
entity.enableAutonomous();
initializeThermalProperties(entity, 293, 0.5, 1, 0.5);
const essence = typeof material.essence === "string" ? material.essence : material.essence?.th || material.essence?.en || "An entity";
entity.remember({
type: "spawn",
subject: "self",
content: { name, essence },
timestamp: Date.now(),
salience: 1
});
const memoryLog = createMemoryLog(entity.uuid);
this.memoryLogs.set(entity.uuid, memoryLog);
const trustSystem = new TrustSystem({
initialTrust: 0.5,
trustThreshold: 0.6
});
trustSystem.setSharePolicy("emotion", "public");
trustSystem.setSharePolicy("memory", "trust");
this.trustSystems.set(entity.uuid, trustSystem);
const entityInfo = {
name,
entity,
previousValence: entity.emotion.valence
};
this.entities.set(name, entityInfo);
return entityInfo;
}
setupEnvironmentSensors() {
this.world.broadcastEvent("system_context", {
platform: process.platform,
arch: process.arch,
nodeVersion: process.version,
context: "terminal"
});
setInterval(() => {
const now = new Date;
const hour = now.getHours();
const minute = now.getMinutes();
let timeOfDay = "night";
if (hour >= 5 && hour < 12)
timeOfDay = "morning";
else if (hour >= 12 && hour < 17)
timeOfDay = "afternoon";
else if (hour >= 17 && hour < 21)
timeOfDay = "evening";
this.world.broadcastEvent("time_update", {
hour,
minute,
timeOfDay,
timestamp: now.toISOString()
});
for (const entityInfo of this.entities.values()) {
if (entityInfo.entity.memory && Math.random() < 0.1) {
entityInfo.entity.remember({
type: "observation",
subject: "time",
content: { timeOfDay, hour },
timestamp: Date.now(),
salience: 0.2
});
}
}
}, 30000);
const startTime = Date.now();
setInterval(() => {
const duration = Date.now() - startTime;
const minutes = Math.floor(duration / 60000);
if (minutes > 0) {
this.world.broadcastEvent("session_duration", {
duration,
minutes
});
for (const entityInfo of this.entities.values()) {
if (entityInfo.entity.memory && minutes % 5 === 0) {
entityInfo.entity.remember({
type: "observation",
subject: "conversation",
content: { duration: `${minutes} minutes` },
timestamp: Date.now(),
salience: 0.3
});
}
}
}
}, 300000);
setInterval(() => {
this.spawnLongingField(this.companionEntity, "traveler");
}, 120000);
setInterval(() => {
const metrics = this.osSensor.getMetrics();
const envMapping = this.osSensor.mapToEnvironment(metrics);
this.environment["config"].baseTemperature = envMapping.temperature;
this.environment["config"].baseHumidity = envMapping.humidity;
this.environment["config"].baseLight = envMapping.light;
this.environment["config"].windVelocity = {
vx: envMapping.windVx,
vy: envMapping.windVy
};
this.world.broadcastEvent("environment_update", {
temperature: envMapping.temperature,
humidity: envMapping.humidity,
light: envMapping.light,
wind: { vx: envMapping.windVx, vy: envMapping.windVy }
});
this.emit("environment", {
metrics,
mapping: envMapping
});
}, 1e4);
setInterval(() => {
this.weather.update(2);
const weatherState = this.weather.getState();
if (weatherState.rain) {
this.environment["config"].baseHumidity = Math.min(1, this.environment["config"].baseHumidity + weatherState.rainIntensity * 0.3);
this.environment["config"].baseLight = Math.max(0.2, this.environment["config"].baseLight * (1 - weatherState.cloudCover));
const windMult = weatherState.windStrength;
this.environment["config"].windVelocity = {
vx: this.environment["config"].windVelocity.vx * windMult,
vy: this.environment["config"].windVelocity.vy * windMult
};
this.world.broadcastEvent("weather_rain", {
intensity: weatherState.rainIntensity,
cloudCover: weatherState.cloudCover,
windStrength: weatherState.windStrength
});
this.emit("weather", {
type: "rain",
intensity: weatherState.rainIntensity,
cloudCover: weatherState.cloudCover
});
for (const entityInfo of this.entities.values()) {
const entity = entityInfo.entity;
entity.emotion = {
valence: Math.max(-1, entity.emotion.valence - 0.05 * weatherState.rainIntensity),
arousal: Math.max(0, entity.emotion.arousal - 0.03 * weatherState.rainIntensity),
dominance: entity.emotion.dominance
};
}
}
}, 2000);
setInterval(() => {
const companion = this.companionEntity.entity;
if (companion.memory?.memories && companion.memory.memories.length > 5) {
const consolidated = this.memoryConsolidation.consolidate(companion.memory.memories);
if (consolidated.length > 0) {
this.emit("memory-consolidation", {
entity: "companion",
count: consolidated.length,
strongest: consolidated.slice(0, 3).map((m) => ({
subject: m.subject,
strength: m.strength.toFixed(2),
rehearsals: m.rehearsalCount
}))
});
}
}
this.memoryConsolidation.applyForgetting(60);
for (const trustSystem of this.trustSystems.values()) {
trustSystem.decayTrust(60);
}
}, 60000);
setInterval(() => {
const allEntities = Array.from(this.entities.values()).map((info) => info.entity);
const companion = this.companionEntity.entity;
const traveler = this.impersonatedEntity.entity;
const dx = traveler.x - companion.x;
const dy = traveler.y - companion.y;
const distance = Math.sqrt(dx * dx + dy * dy);
const collisionRadius = 50;
if (distance < collisionRadius) {
this.emit("collision", {
entities: ["companion", "traveler"],
distance: distance.toFixed(1)
});
this.world.broadcastEvent("entity_collision", {
a: "companion",
b: "traveler",
distance
});
}
this.energySystem.updateEntityTransfer(allEntities, 1);
this.energySystem.updateEnvironmentTransfer(allEntities, this.environment, 1);
for (const entity of allEntities) {
const physProps = this.coupler.emotionToPhysics(entity.emotion);
if (physProps && physProps.speed !== undefined) {
const baseSpeed = 2;
const emotionalSpeed = baseSpeed * physProps.speed;
const angle = Math.random() * Math.PI * 2;
const dx2 = Math.cos(angle) * emotionalSpeed;
const dy2 = Math.sin(angle) * emotionalSpeed;
entity.x = Math.max(0, Math.min(800, entity.x + dx2));
entity.y = Math.max(0, Math.min(600, entity.y + dy2));
entity["_physProps"] = physProps;
}
}
if (Date.now() % 5000 < 1000) {
this.emit("thermal", {
companion: {
temperature: (companion.temperature - 273).toFixed(1) + "°C",
humidity: ((companion.humidity || 0) * 100).toFixed(0) + "%"
},
traveler: {
temperature: (traveler.temperature - 273).toFixed(1) + "°C",
humidity: ((traveler.humidity || 0) * 100).toFixed(0) + "%"
}
});
}
}, 1000);
setInterval(() => {
const allEntities = Array.from(this.entities.values()).map((info) => info.entity);
const stats = CollectiveIntelligence.calculateStats(allEntities);
const patterns = CollectiveIntelligence.detectPatterns(allEntities);
const collectiveEmotion = CollectiveIntelligence.calculateCollectiveEmotion(allEntities);
this.emit("world-mind", {
stats,
patterns,
collectiveEmotion
});
}, 30000);
}
getAllEntities() {
return Array.from(this.entities.values());
}
getEntityInfo() {
return this.companionEntity || null;
}
getGreeting() {
const entity = this.companionEntity.entity;
const travelerMemories = entity.memory?.recall({ subject: "traveler" }) || [];
if (travelerMemories.length === 0) {
return entity.speak("intro") || "Hi...";
} else {
const greeting = entity.speak("greeting_familiar") || "Welcome back!";
return greeting;
}
}
async handleUserMessage(message) {
const traveler = this.impersonatedEntity.entity;
const companion = this.companionEntity.entity;
const now = Date.now();
const silenceDuration = this.lastMessageTime ? (now - this.lastMessageTime) / 1000 : 0;
companion.updateTriggerContext({
userMessage: message,
userSilenceDuration: silenceDuration
});
companion.checkEmotionTriggers();
this.lastMessageTime = now;
const newWords = this.vocabularyTracker.detectNewWords(message);
if (newWords.length > 0) {
this.emit("vocab", { words: newWords });
for (const word of newWords.slice(0, 2)) {
companion.remember({
type: "observation",
subject: "vocabulary",
content: { word, source: "traveler" },
timestamp: Date.now(),
salience: 0.6
});
}
}
const context = this.contextAnalyzer.analyzeIntent(message, companion);
if (context.emotionHint) {
const current = companion.emotion;
companion.emotion = {
valence: (current.valence + context.emotionHint.valence) / 2,
arousal: (current.arousal + context.emotionHint.arousal) / 2,
dominance: current.dominance
};
}
if (traveler.memory) {
traveler.remember({
type: "interaction",
subject: this.companionEntity.name,
content: { message, intent: context.intent },
timestamp: Date.now(),
salience: 0.7
});
}
companion.remember({
type: "interaction",
subject: "traveler",
content: { message, intent: context.intent },
timestamp: Date.now(),
salience: 0.7
});
if (companion.cognitiveLinks && traveler.cognitiveLinks) {
companion.connectTo(traveler, { strength: 0.7, bidirectional: true });
this.emit("link", {
from: this.companionEntity.name,
to: this.impersonatedEntity.name,
strength: 0.7
});
}
const response = await this.getEntityResponse(message, context);
companion.remember({
type: "interaction",
subject: "self",
content: { response },
timestamp: Date.now(),
salience: 0.4
});
this.world.recordSpeech(companion, response);
const emotionDelta = companion.emotion.valence - this.companionEntity.previousValence;
if (Math.abs(emotionDelta) > 0.1 && companion.learning) {
companion.learning.addExperience({
timestamp: Date.now(),
action: "respond",
context: { intent: context.intent },
outcome: emotionDelta > 0 ? "positive" : "negative",
reward: emotionDelta,
success: emotionDelta > 0
});
}
this.vocabularyTracker.incrementConversation();
this.growthTracker.update({
vocabularySize: this.vocabularyTracker.getVocabularySize(),
conversationCount: this.vocabularyTracker.toJSON().conversationCount,
memoryCount: companion.memory?.memories?.length || 0
});
for (const keyword of context.keywords) {
if (keyword.length > 3) {
this.growthTracker.learnConcept(keyword);
}
}
this.companionEntity.previousValence = companion.emotion.valence;
const valenceDiff = Math.abs(companion.emotion.valence - traveler.emotion.valence);
const arousalDiff = Math.abs(companion.emotion.arousal - traveler.emotion.arousal);
const alignment = 1 - (valenceDiff + arousalDiff) / 2;
if (alignment > 0.6) {
this.spawnSyncMoment(this.companionEntity, this.impersonatedEntity);
}
return {
name: this.companionEntity.name,
response,
emotion: { ...companion.emotion },
previousValence: this.companionEntity.previousValence,
newWordsLearned: newWords
};
}
async getEntityResponse(userMessage, context) {
const entity = this.companionEntity.entity;
let response;
const categoryMap = {
greeting: "intro",
praise: "happy",
criticism: "sad",
question: "thinking",
farewell: "intro",
curiosity: "curious",
excitement: "excited",
confusion: "confused",
neutral: "self_monologue",
introspection: "self_monologue"
};
const category = categoryMap[context.intent];
if (category) {
response = entity.speak(category);
}
if (!response && context.keywords.some((k) => !this.vocabularyTracker.canUse(k))) {
response = entity.speak("learned_new_word");
}
if (!response && context.relevantMemories.length === 0 && context.intent === "question") {
response = entity.speak("confused");
}
if (!response) {
const emotion = entity.emotion;
if (emotion.valence > 0.5) {
response = entity.speak("happy");
} else if (emotion.valence < -0.3) {
response = entity.speak("sad");
} else if (emotion.arousal > 0.6) {
response = entity.speak("excited");
} else if (emotion.arousal < 0.3) {
response = entity.speak("tired");
} else {
response = entity.speak("curious");
}
}
if (!response && this.vocabularyTracker.getVocabularySize() >= 20) {
let vocabularyPool = this.vocabularyTracker.toJSON().knownWords;
if (this.world.lexicon) {
const patterns = this.world.lexicon.getAll();
if (patterns.length > 0) {
const frequentPatterns = patterns.filter((p) => p.weight > 0.3).map((p) => p.phrase);
vocabularyPool = [...vocabularyPool, ...frequentPatterns];
}
}
const envState = this.environment.getState(entity.x, entity.y);
const tempCelsius = envState.temperature - 273;
if (tempCelsius > 30)
vocabularyPool.push("ร้อน", "hot", "\uD83E\uDD75");
else if (tempCelsius < 15)
vocabularyPool.push("หนาว", "cold", "\uD83E\uDD76");
if (envState.humidity > 0.7)
vocabularyPool.push("ชื้น", "humid", "\uD83D\uDCA7");
if (envState.light < 0.4)
vocabularyPool.push("มืด", "dark", "\uD83C\uDF19");
else if (envState.light > 0.8)
vocabularyPool.push("สว่าง", "bright", "☀️");
const weatherState = this.weather.getState();
if (weatherState.rain) {
vocabularyPool.push("ฝนตก", "rain", "\uD83C\uDF27️", "wet", "เปียก");
if (weatherState.rainIntensity > 0.7) {
vocabularyPool.push("heavy rain", "ฝนหนัก", "⛈️");
}
}
response = this.protoLangGenerator.generateResponse(userMessage, {
vocabularyPool,
emotion: entity.emotion,
minWords: 1,
maxWords: 4,
allowParticles: true,
allowEmoji: true,
creativity: 0.6
});
if (response) {
this.emit("proto-lang", {
message: response,
vocabularySize: vocabularyPool.length,
environment: {
temperature: tempCelsius.toFixed(1) + "°C",
humidity: (envState.humidity * 100).toFixed(0) + "%",
light: (envState.light * 100).toFixed(0) + "%"
}
});
}
}
if (!response && this.world.languageGenerator) {
try {
this.emit("llm", { action: "fallback" });
const prompt = this.promptBuilder.buildPrompt(entity, userMessage, context, this.vocabularyTracker);
const llmResponse = await this.world.languageGenerator.generate({
speaker: entity,
context: prompt
});
response = llmResponse.text;
} catch (error) {
this.emit("error", { type: "llm", message: error instanceof Error ? error.message : String(error) });
response = entity.speak("intro") || "...";
}
}
if (!response) {
response = entity.speak("intro") || "...";
}
return response;
}
async spawnFriend(description) {
if (!this.world.languageGenerator) {
return null;
}
const prompt = description ? `Create a companion entity for a chat space. Description: "${description}"
Generate a JSON object with:
- essence (Thai + English description of personality)
- languageProfile (native language + weights)
- emotion.base (initial valence/arousal/dominance)
- dialogue (intro, greeting, confused, etc.)
Be creative and make them interesting!` : `Create a new companion entity that would be a good friend for an INTP kid. Make them different but complementary.
Generate a JSON object following the same format as companion.mdm.`;
try {
const response = await this.world.languageGenerator.generate({
speaker: this.primaryEntity.entity,
context: prompt
});
const generatedData = JSON.parse(response.text);
const material = {
$schema: "mdspec/v5.7",
material: `entity.friend_${Date.now()}`,
...generatedData
};
const entity = this.world.spawn(material, 200, 200);
const name = generatedData.essence?.en?.split(" ")[0] || `Friend${this.entities.size + 1}`;
entity.enable("memory", "learning", "relationships");
entity.enableAutonomous();
const entityInfo = {
name,
entity,
previousValence: entity.emotion.valence
};
this.entities.set(name, entityInfo);
if (this.primaryEntity.entity.memory) {
this.primaryEntity.entity.remember({
type: "interaction",
subject: name,
content: { action: "met", essence: generatedData.essence },
timestamp: Date.now(),
salience: 0.8
});
}
return entityInfo;
} catch (error) {
if (!this.silentMode)
console.error("[Spawn Error]", error);
return null;
}
}
async generateAutonomousMessage() {
const companion = this.companionEntity.entity;
if (!companion.isAutonomous()) {
return null;
}
let response = companion.speak("self_monologue");
if (!response) {
const emotion = companion.emotion;
if (emotion.valence > 0.5) {
response = companion.speak("happy");
} else if (emotion.valence < -0.3) {
response = companion.speak("sad");
} else {
response = companion.speak("curious");
}
}
if (!response && this.vocabularyTracker.getVocabularySize() >= 20) {
let vocabularyPool = this.vocabularyTracker.toJSON().knownWords;
if (this.world.lexicon) {
const patterns = this.world.lexicon.getAll();
if (patterns.length > 0) {
const frequentPatterns = patterns.filter((p) => p.weight > 0.3).map((p) => p.phrase);
vocabularyPool = [...vocabularyPool, ...frequentPatterns];
}
}
const envState = this.environment.getState(companion.x, companion.y);
const tempCelsius = envState.temperature - 273;
if (tempCelsius > 30)
vocabularyPool.push("ร้อน", "hot", "\uD83E\uDD75");
else if (tempCelsius < 15)
vocabularyPool.push("หนาว", "cold", "\uD83E\uDD76");
if (envState.humidity > 0.7)
vocabularyPool.push("ชื้น", "humid", "\uD83D\uDCA7");
if (envState.light < 0.4)
vocabularyPool.push("มืด", "dark", "\uD83C\uDF19");
const weatherState = this.weather.getState();
if (weatherState.rain) {
vocabularyPool.push("ฝนตก", "rain", "\uD83C\uDF27️");
}
response = this.protoLangGenerator.generate(companion.emotion, vocabularyPool, undefined, envState);
}
if (!response) {
return null;
}
return {
name: this.companionEntity.name,
response,
emotion: { ...companion.emotion }
};
}
getStatusSummary() {
let output = `\uD83D\uDCCA Status
`;
for (const entityInfo of this.entities.values()) {
const entity = entityInfo.entity;
const emotion = entity.emotion;
const memoryCount = entity.memory?.count() || 0;
const isImpersonated = entityInfo === this.impersonatedEntity ? " (YOU)" : "";
output += `${entityInfo.name}${isImpersonated}:
`;
output += ` Emotion: ${emotion.valence.toFixed(2)} (${this.getEmotionWord(emotion)})
`;
output += ` Arousal: ${emotion.arousal.toFixed(2)}
`;
output += ` Memories: ${memoryCount}
`;
}
const vocabStats = this.vocabularyTracker.getStats();
output += `\uD83D\uDCDA Vocabulary: ${vocabStats.total} words (+${vocabStats.learnedWords} learned)
`;
output += `\uD83D\uDCAC Conversations: ${vocabStats.conversationCount}
`;
const lexiconStats = this.world.getLexiconStats();
if (lexiconStats && lexiconStats.totalTerms > 0) {
output += `
\uD83D\uDDE3️