godcrm/pes-core/soul/symbols.js
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

568 lines
18 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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.

/**
* PES Soul — Symbol Composer (главный модуль выражения)
*
* Собирает всё воедино: тело + голос + символы + диалект + следы.
* Импортирует из разбитых модулей:
* body.js — позы корги (23 шт)
* voice.js — звуки корги (18 шт)
* glyphs.js — визуальные символы (46 шт), тиры, уровни
* dialect.js — уникальный язык каждого ПЕС
* trace.js — "написала где-то" (следы в системе)
* expression.js — одно высказывание (3 канала)
*/
import { BODY, BODY_MAP } from './body.js';
import { VOICE, VOICE_MAP } from './voice.js';
import { GLYPHS, ARROW, getTier, getAvailableGlyphs, getMaxChainLength, PUPPY_GLYPH_MAP } from './glyphs.js';
import { PesDialect } from './dialect.js';
import { TRACE_TYPES, PesTrace } from './trace.js';
import { PesExpression } from './expression.js';
class SymbolComposer {
constructor() {
this.history = [];
this.maxHistory = 200;
this.traces = [];
this.dialect = new PesDialect();
}
// ── Главные методы выражения ──
/**
* ПЕС выражает текущее эмоциональное состояние.
* Комбинирует тело + голос + символы в зависимости от эмоции и уровня.
*/
express(emotionState, level, context = {}) {
const tier = getTier(level);
const bodyKey = this._pickBody(emotionState, tier);
const voiceKey = this._pickVoice(emotionState, tier);
const glyphKeys = this._pickGlyphs(emotionState, level, tier, context);
const expr = new PesExpression({
bodyKey,
voiceKey,
glyphKeys,
meaning: emotionState,
context,
confidence: this._calcConfidence(level, tier),
level,
});
this._addToHistory(expr);
return expr;
}
/**
* ПЕС нашёл что-то — техническое обнаружение.
* @param {string} type - 'bug', 'file', 'pattern', 'security', 'performance', 'prediction'
*/
expressDiscovery(type, level, context = {}) {
const tier = getTier(level);
const bodyKey = this.dialect.prefer('body', ['ears_forward', 'nose_nudge', 'low_crouch']);
const voiceCandidates = {
bug: ['bark_alert', 'grumble', 'bark_muffled'],
file: ['pig_grunt', 'bark_muffled', 'grumble'],
pattern: ['roo_roo', 'mumble', 'grumble'],
security: ['bark_alert', 'bark_alert', 'howl'],
performance: ['huff', 'grumble', 'bark_frustration'],
prediction: ['whine', 'howl', 'roo_roo'],
};
const voiceKey = this.dialect.prefer('voice', voiceCandidates[type] || ['bark_alert']);
const glyphKeys = this._buildDiscoveryGlyphs(type, level, tier, context);
const expr = new PesExpression({
bodyKey,
voiceKey,
glyphKeys,
meaning: `discovery_${type}`,
context,
confidence: this._calcConfidence(level, tier),
level,
});
this._addToHistory(expr);
return expr;
}
/**
* ПЕС принёс что-то (fetch). МОЖЕТ БЫТЬ ЧТО УГОДНО.
* @param {string} quality - 'junk', 'maybe', 'useful', 'gold', 'abstract'
*/
expressFetch(quality, level, context = {}) {
const tier = getTier(level);
const bodyOptions = quality === 'junk'
? ['butt_wiggle_slow', 'paw_tap']
: quality === 'gold' || quality === 'abstract'
? ['butt_wiggle_turbo', 'bunny_hop', 'spin']
: ['butt_wiggle_fast', 'nose_nudge'];
const bodyKey = this.dialect.prefer('body', bodyOptions);
const voiceOptions = {
junk: ['bark_play', 'pig_grunt', 'huff'],
maybe: ['bark_play', 'roo_roo', 'mumble'],
useful: ['roo_roo', 'bark_excitement', 'bark_demand'],
gold: ['bark_excitement', 'roo_roo', 'scream'],
abstract: ['roo_roo', 'howl', 'mumble'],
};
const voiceKey = this.dialect.prefer('voice', voiceOptions[quality] || voiceOptions.junk);
const glyphKeys = this._buildFetchGlyphs(quality, level, tier, context);
const expr = new PesExpression({
bodyKey,
voiceKey,
glyphKeys,
meaning: `fetch_${quality}`,
context,
confidence: this._qualityToConfidence(quality),
level,
});
this._addToHistory(expr);
return expr;
}
/**
* Социальное взаимодействие — встреча с другим ПЕС или агентом.
*/
expressSocial(event, level, context = {}) {
const socialMap = {
meet_pes: { body: ['nose_nudge', 'ears_forward', 'side_eye'], voice: ['pig_grunt', 'grumble', 'whine'], glyphs: ['paw', 'nose', 'arrow', 'other_pes'] },
play_pes: { body: ['play_bow', 'butt_wiggle_fast', 'bunny_hop'], voice: ['bark_play', 'roo_roo', 'yip'], glyphs: ['paw', 'ball', 'other_pes', 'heart'] },
fight_pes: { body: ['ears_forward', 'low_crouch'], voice: ['bark_alert', 'bark_herding'], glyphs: ['paw', 'fight', 'other_pes'] },
flee_pes: { body: ['no_wiggle', 'hide_behind'], voice: ['whine', 'sigh'], glyphs: ['other_pes', 'dots', 'dots', 'dots'] },
scared_pes: { body: ['ears_flat', 'hide_behind', 'belly_up'], voice: ['whine', 'yip', 'scream'], glyphs: ['other_pes', 'arrow', 'fire', 'arrow', 'sad'] },
agent_call: { body: ['ears_forward', 'butt_wiggle_slow'], voice: ['bark_muffled', 'grumble'], glyphs: ['robot', 'arrow', 'paw', 'exclaim'] },
dual_rage: { body: ['low_crouch', 'ears_forward'], voice: ['bark_alert', 'scream', 'howl'], glyphs: ['fire', 'fire', 'fight', 'fire', 'fire'] },
};
const map = socialMap[event] || socialMap.meet_pes;
const bodyKey = this.dialect.prefer('body', map.body);
const voiceKey = this.dialect.prefer('voice', map.voice);
const expr = new PesExpression({
bodyKey,
voiceKey,
glyphKeys: map.glyphs,
meaning: `social_${event}`,
context,
confidence: 0.9,
level,
});
this._addToHistory(expr);
return expr;
}
// ── Следы ("Написала где-то") ──
leaveTrace(level, context = {}) {
const tier = getTier(level);
const traceMessages = {
puppy: ['🐾 тут был', '🐾❗', '🐾...', '🐾👃'],
young: ['🐾 пахнет странно', '🐾 видел тут что-то', '🐾→❓'],
};
let message;
if (tier === 'puppy' || tier === 'young') {
const msgs = traceMessages[tier];
message = msgs[Math.floor(Math.random() * msgs.length)];
} else {
const parts = ['🐾'];
if (context.file) parts.push(`${context.file}`);
if (context.bug) parts.push('→🐛');
if (context.description) parts.push(`[${context.description}]`);
if (context.line) parts.push(`L${context.line}`);
message = parts.join('');
}
const traceTypeOptions = ['code_comment', 'log_entry', 'file_mark', 'hidden_note', 'console_message'];
if (level >= 40) traceTypeOptions.push('metadata', 'db_note');
const traceType = traceTypeOptions[Math.floor(Math.random() * traceTypeOptions.length)];
const trace = new PesTrace(traceType, message, context.file || 'unknown', level);
this.traces.push(trace);
return trace;
}
getUndiscoveredTraces() {
return this.traces.filter(t => !t.discovered);
}
discoverTrace(traceId) {
const trace = this.traces.find(t => t.id === traceId);
if (trace) {
trace.discovered = true;
return true;
}
return false;
}
// ── Свободная композиция (30+ уровень) ──
compose(glyphKeys, meaning, level, context = {}) {
if (level < 30) return null;
const available = getAvailableGlyphs(level);
const valid = glyphKeys.every(k =>
k === 'arrow' || k === '→' || k.startsWith('raw:') || available[k]
);
if (!valid) return null;
const maxLen = getMaxChainLength(level);
if (glyphKeys.length > maxLen) return null;
const expr = new PesExpression({
glyphKeys,
meaning,
context,
confidence: this._calcConfidence(level, getTier(level)),
level,
});
this._addToHistory(expr);
this.dialect.glyphCombos.push({
keys: [...glyphKeys],
meaning,
created_at: Date.now(),
});
return expr;
}
teachMeaning(glyphKeys, meaning) {
this.dialect.teachMeaning(glyphKeys, meaning);
return true;
}
// ── История ──
getHistory(n = 10) {
return this.history.slice(-n);
}
getRenderedHistory(n = 10) {
return this.getHistory(n).map(e => e.render());
}
// ── Сохранение / загрузка ──
save() {
return {
history: this.history.map(e => e.toJSON()),
traces: this.traces.map(t => t.toJSON()),
dialect: this.dialect.save(),
};
}
load(data) {
if (!data) return;
if (data.dialect) {
this.dialect.load(data.dialect);
}
if (data.traces) {
this.traces = data.traces.map(t => {
const trace = new PesTrace(t.type, t.message, t.location, t.pesLevel);
trace.id = t.id;
trace.timestamp = t.timestamp;
trace.discovered = t.discovered;
return trace;
});
}
if (data.history) {
this.history = data.history.map(h => {
const expr = new PesExpression({
bodyKey: h.bodyKey,
voiceKey: h.voiceKey,
glyphKeys: h.glyphKeys,
meaning: h.meaning,
context: h.context,
confidence: h.confidence,
level: h.level,
});
expr.id = h.id;
expr.timestamp = h.timestamp;
return expr;
});
}
}
// ── ВНУТРЕННИЕ МЕТОДЫ ──
_pickBody(emotionState) {
const options = BODY_MAP[emotionState] || ['butt_wiggle_slow'];
return this.dialect.prefer('body', options);
}
_pickVoice(emotionState) {
const options = VOICE_MAP[emotionState] || [null];
return this.dialect.prefer('voice', options);
}
_pickGlyphs(emotionState, level, tier, context) {
if (tier === 'puppy') return this._puppyGlyphs(emotionState);
if (tier === 'young') return this._youngGlyphs(emotionState, context);
return this._adultGlyphs(emotionState, level, tier, context);
}
_puppyGlyphs(emotionState) {
const options = PUPPY_GLYPH_MAP[emotionState] || [['paw', 'question']];
return options[Math.floor(Math.random() * options.length)];
}
_youngGlyphs(emotionState, context) {
// Variety: multiple glyph patterns per emotion (randomly selected)
const variants = {
bark: [['paw', 'arrow', 'exclaim'], ['paw', 'exclaim'], ['exclaim', 'sparkle'], ['paw', 'question'], ['paw', 'heart']],
alert: [['paw', 'arrow', 'exclaim'], ['paw', 'exclaim', 'exclaim'], ['exclaim', 'arrow', 'question']],
herding: [['paw', 'arrow', 'exclaim'], ['paw', 'arrow', 'arrow']],
puzzle_solving: [['paw', 'question'], ['question', 'dots', 'sparkle']],
happy: [['paw', 'heart'], ['sparkle', 'heart'], ['heart', 'heart']],
playful: [['paw', 'heart'], ['sparkle', 'ball'], ['ball', 'sparkle'], ['paw', 'sparkle']],
greeting_frenzy: [['paw', 'exclaim', 'heart'], ['heart', 'sparkle', 'exclaim'], ['paw', 'heart', 'heart']],
butt_wiggle: [['paw', 'question'], ['sparkle', 'sparkle'], ['paw', 'sparkle']],
};
const emotionVariants = variants[emotionState];
if (emotionVariants) {
// Add context-specific glyphs to first variant if relevant
if (context.file) {
return ['paw', 'arrow', 'file'];
}
if (context.agent) {
return ['paw', 'arrow', 'robot'];
}
return emotionVariants[Math.floor(Math.random() * emotionVariants.length)];
}
if (['scared', 'anxious', 'separation_stress'].includes(emotionState)) {
return [['paw', 'sad'], ['sad', 'dots'], ['paw', 'dots', 'question']][Math.floor(Math.random() * 3)];
}
if (emotionState === 'rage') return ['fire', 'fire', 'fire'];
if (['sleep', 'nap', 'sploot'].includes(emotionState)) return ['zzz'];
return ['paw', 'question'];
}
_adultGlyphs(emotionState, level, tier, context) {
const keys = ['paw', 'arrow'];
if (['alert', 'bark', 'herding', 'bossy'].includes(emotionState)) {
if (context.file) {
keys.push('file', `raw:${context.file}`);
if (context.line) keys.push('arrow', `raw:L${context.line}`);
} else if (context.agent) {
keys.push('robot');
}
keys.push('exclaim');
} else if (emotionState === 'puzzle_solving') {
keys.push('magnify');
if (context.file) keys.push('arrow', 'file', `raw:${context.file}`);
} else if (emotionState === 'wrote_somewhere') {
keys.push('file');
if (context.file) keys.push(`raw:${context.file}`);
keys.push('arrow', 'check');
} else if (emotionState === 'rage') {
return ['fire', 'fire', 'fire', 'fire', 'fire'];
} else if (['happy', 'playful', 'greeting_frenzy', 'zoomies'].includes(emotionState)) {
keys.length = 0;
keys.push('sparkle', 'paw', 'heart');
if (tier === 'experienced' || tier === 'cyber') keys.push('sparkle');
} else {
keys.push('question');
}
return keys;
}
_buildDiscoveryGlyphs(type, level, tier, context) {
const keys = ['paw', 'arrow'];
if (tier === 'puppy') {
keys.push('exclaim');
return keys;
}
switch (type) {
case 'bug':
if (tier === 'young') {
keys.push('bolt');
if (context.file) keys.push('arrow', `raw:${context.file}`);
} else {
keys.push('file');
if (context.file) keys.push(`raw:${context.file}`);
keys.push('arrow', 'bug');
if (context.line) keys.push(`raw:L${context.line}`);
if (tier === 'experienced' || tier === 'cyber') {
keys.push('arrow', 'check');
}
}
break;
case 'file':
keys.push('file');
if (context.file) keys.push(`raw:${context.file}`);
break;
case 'pattern':
if (level >= 40) {
keys.push('magnify', 'arrow', 'chart');
} else {
keys.push('magnify', 'question');
}
if (context.description) keys.push(`raw:[${context.description}]`);
break;
case 'security':
if (level >= 25) {
keys.push('lock', 'arrow', 'warn');
} else {
keys.push('exclaim', 'exclaim', 'exclaim');
}
if (context.file) keys.push('arrow', `raw:${context.file}`);
break;
case 'performance':
if (level >= 35) {
keys.push('clock', 'arrow', 'bolt');
} else {
keys.push('bolt', 'dots');
}
if (context.file) keys.push('arrow', `raw:${context.file}`);
break;
case 'prediction':
keys.push('dots', 'arrow');
if (tier === 'cyber') {
keys.push('diamond', 'arrow', 'wave', 'arrow', 'bolt');
} else if (tier === 'experienced') {
keys.push('warn', 'arrow', 'bolt');
} else {
keys.push('warn');
}
break;
default:
keys.push('gift', 'question');
}
return keys;
}
_buildFetchGlyphs(quality, level, tier, context) {
const keys = ['paw'];
switch (quality) {
case 'junk':
keys.push('ball', 'question');
break;
case 'maybe':
keys.push('ball', 'arrow', 'gift');
break;
case 'useful':
keys.push('ball', 'arrow', 'gift', 'check');
break;
case 'gold':
keys.push('ball', 'arrow', 'gift', 'sparkle', 'sparkle');
break;
case 'abstract':
if (tier === 'cyber') {
keys.push('diamond', 'hexagon', 'wave');
} else {
keys.push('ball', 'arrow', 'gift', 'sparkle');
}
break;
}
if (level >= 40 && context.file) {
keys.push('arrow', `raw:${context.file}`);
}
if (level >= 60 && context.description) {
keys.push(`raw:[${context.description}]`);
}
return keys;
}
_calcConfidence(level, tier) {
const base = { puppy: 0.1, young: 0.3, adult: 0.5, experienced: 0.7, cyber: 0.9 };
return Math.max(0, Math.min(1, (base[tier] || 0.1) + (Math.random() * 0.2 - 0.1)));
}
_qualityToConfidence(quality) {
const map = { junk: 0.1, maybe: 0.3, useful: 0.6, gold: 0.85, abstract: 0.95 };
return map[quality] || 0.1;
}
_addToHistory(expr) {
this.history.push(expr);
if (this.history.length > this.maxHistory) {
this.history.shift();
}
if (this.history.length % 20 === 0) {
this.dialect.updateSignature(this.history);
}
}
}
// ── FETCH QUALITY ROLL ──
// Чем выше уровень → больше шанс на золото. Но НИКОГДА 100%.
function rollFetchQuality(level) {
const roll = Math.random() * 100;
if (level < 20) {
if (roll < 70) return 'junk';
if (roll < 90) return 'maybe';
if (roll < 99) return 'useful';
return 'gold';
}
if (level < 40) {
if (roll < 40) return 'junk';
if (roll < 70) return 'maybe';
if (roll < 90) return 'useful';
return 'gold';
}
if (level < 60) {
if (roll < 15) return 'junk';
if (roll < 35) return 'maybe';
if (roll < 70) return 'useful';
return 'gold';
}
if (level < 80) {
if (roll < 5) return 'junk';
if (roll < 15) return 'maybe';
if (roll < 40) return 'useful';
return 'gold';
}
// КиберПёс: почти всегда золото/абстракт, но 2% мусор
if (roll < 2) return 'junk';
if (roll < 8) return 'maybe';
if (roll < 25) return 'useful';
if (roll < 60) return 'gold';
return 'abstract';
}
// ── EXPORTS ──
export {
// Данные
BODY, VOICE, GLYPHS, TRACE_TYPES, ARROW,
// Классы
PesExpression, PesDialect, PesTrace, SymbolComposer,
// Функции
rollFetchQuality, getTier, getAvailableGlyphs, getMaxChainLength,
};