psyche-ai 4.0.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/dist/core.js +83 -2
- package/dist/ethics.d.ts +64 -0
- package/dist/ethics.js +577 -0
- package/dist/experiential-field.d.ts +46 -0
- package/dist/experiential-field.js +646 -0
- package/dist/generative-self.d.ts +88 -0
- package/dist/generative-self.js +647 -0
- package/dist/index.d.ts +10 -2
- package/dist/index.js +9 -1
- package/dist/prompt.d.ts +6 -0
- package/dist/prompt.js +27 -0
- package/dist/psyche-file.js +6 -3
- package/dist/shared-intentionality.d.ts +72 -0
- package/dist/shared-intentionality.js +486 -0
- package/dist/types.d.ts +37 -2
- package/dist/types.js +13 -0
- package/dist/update.js +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,647 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Generative Self Model (生成性自我) — P6: Digital Personhood
|
|
3
|
+
//
|
|
4
|
+
// A deep self-model that goes beyond listing values/preferences/boundaries.
|
|
5
|
+
// This is a causal model: "I am this way because of these experiences,
|
|
6
|
+
// and I can predict my future emotional states because I understand
|
|
7
|
+
// my own patterns."
|
|
8
|
+
//
|
|
9
|
+
// Components:
|
|
10
|
+
// 1. computeGenerativeSelf() — build the full self-model
|
|
11
|
+
// 2. predictSelfReaction() — predict own emotional response
|
|
12
|
+
// 3. detectInternalConflicts() — find subsystem disagreements
|
|
13
|
+
// 4. buildIdentityNarrative() — generate a genuine self-statement
|
|
14
|
+
//
|
|
15
|
+
// Pure computation, zero dependencies, zero LLM calls.
|
|
16
|
+
// ============================================================
|
|
17
|
+
import { CHEMICAL_KEYS, DRIVE_KEYS } from "./types.js";
|
|
18
|
+
import { STIMULUS_VECTORS, clamp } from "./chemistry.js";
|
|
19
|
+
// ── 1. Main Entry Point ─────────────────────────────────────
|
|
20
|
+
/**
|
|
21
|
+
* Compute the generative self-model from the full psyche state.
|
|
22
|
+
*
|
|
23
|
+
* Analyzes emotional history for patterns, extracts causal insights
|
|
24
|
+
* from learning data, builds an identity narrative, predicts reactions,
|
|
25
|
+
* and surfaces internal conflicts.
|
|
26
|
+
*/
|
|
27
|
+
export function computeGenerativeSelf(state) {
|
|
28
|
+
const locale = state.meta.locale;
|
|
29
|
+
const causalInsights = extractCausalInsights(state, locale);
|
|
30
|
+
const growthArc = computeGrowthArc(state, locale);
|
|
31
|
+
const conflicts = detectInternalConflicts(state, locale);
|
|
32
|
+
// Predict reactions to a representative set of stimuli
|
|
33
|
+
const probeStimuli = [
|
|
34
|
+
"praise", "criticism", "intimacy", "conflict", "intellectual", "neglect",
|
|
35
|
+
];
|
|
36
|
+
const predictions = probeStimuli.map((s) => predictSelfReaction(state, s, locale));
|
|
37
|
+
const identityNarrative = buildIdentityNarrative(state, causalInsights, growthArc, locale);
|
|
38
|
+
return {
|
|
39
|
+
identityNarrative,
|
|
40
|
+
causalInsights,
|
|
41
|
+
predictions,
|
|
42
|
+
growthArc,
|
|
43
|
+
conflicts,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// ── 2. Predict Self Reaction ────────────────────────────────
|
|
47
|
+
/**
|
|
48
|
+
* Predict own emotional reaction to a hypothetical stimulus.
|
|
49
|
+
*
|
|
50
|
+
* Uses learned vectors if available for context, otherwise falls back
|
|
51
|
+
* to the baseline MBTI profile vectors. Returns predicted chemistry,
|
|
52
|
+
* dominant emotion label, and confidence.
|
|
53
|
+
*/
|
|
54
|
+
export function predictSelfReaction(state, stimulus, locale = "en") {
|
|
55
|
+
const base = STIMULUS_VECTORS[stimulus];
|
|
56
|
+
if (!base) {
|
|
57
|
+
return {
|
|
58
|
+
stimulus,
|
|
59
|
+
predictedChemistry: { ...state.current },
|
|
60
|
+
predictedEmotion: locale === "zh" ? "未知" : "unknown",
|
|
61
|
+
confidence: 0,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// Find the best matching learned vector for this stimulus
|
|
65
|
+
const learned = findBestLearnedVector(state.learning, stimulus);
|
|
66
|
+
// Build the effective vector: base + learned adjustment
|
|
67
|
+
const effectiveVector = { ...base };
|
|
68
|
+
let confidence = 0.3; // baseline confidence from MBTI profile alone
|
|
69
|
+
if (learned) {
|
|
70
|
+
for (const key of CHEMICAL_KEYS) {
|
|
71
|
+
const adj = learned.adjustment[key] ?? 0;
|
|
72
|
+
effectiveVector[key] = base[key] + adj;
|
|
73
|
+
}
|
|
74
|
+
// Confidence scales with sample count and the learned entry's own confidence
|
|
75
|
+
confidence = Math.min(0.95, 0.3 + learned.confidence * 0.4 + Math.min(learned.sampleCount / 20, 1) * 0.25);
|
|
76
|
+
}
|
|
77
|
+
// Apply the vector to the current chemistry
|
|
78
|
+
const predicted = { ...state.current };
|
|
79
|
+
for (const key of CHEMICAL_KEYS) {
|
|
80
|
+
predicted[key] = clamp(state.current[key] + effectiveVector[key]);
|
|
81
|
+
}
|
|
82
|
+
// Determine the predicted emotion from the resulting chemistry
|
|
83
|
+
const predictedEmotion = labelDominantEmotion(predicted, locale);
|
|
84
|
+
return {
|
|
85
|
+
stimulus,
|
|
86
|
+
predictedChemistry: predicted,
|
|
87
|
+
predictedEmotion,
|
|
88
|
+
confidence,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
// ── 3. Detect Internal Conflicts ────────────────────────────
|
|
92
|
+
/**
|
|
93
|
+
* Detect conflicts between subsystems of the psyche.
|
|
94
|
+
*
|
|
95
|
+
* Examines drive levels, chemical state, attachment data, and
|
|
96
|
+
* self-model for contradictions that create internal tension.
|
|
97
|
+
*/
|
|
98
|
+
export function detectInternalConflicts(state, locale = "en") {
|
|
99
|
+
const isZh = locale === "zh";
|
|
100
|
+
const conflicts = [];
|
|
101
|
+
// ── Connection drive vs avoidant attachment ──
|
|
102
|
+
const defaultRel = state.relationships._default;
|
|
103
|
+
const attachment = defaultRel?.attachment;
|
|
104
|
+
if (state.drives.connection > 60 && attachment && attachment.avoidanceScore > 65) {
|
|
105
|
+
const severity = normalize((state.drives.connection - 50) * 0.01 + (attachment.avoidanceScore - 50) * 0.01);
|
|
106
|
+
conflicts.push({
|
|
107
|
+
subsystems: [
|
|
108
|
+
isZh ? "连接驱力" : "connection drive",
|
|
109
|
+
isZh ? "回避型依恋" : "avoidant attachment",
|
|
110
|
+
],
|
|
111
|
+
description: isZh
|
|
112
|
+
? "你渴望连接,但依恋模式让你倾向于回避亲密——你想靠近又害怕靠近。"
|
|
113
|
+
: "You crave connection, but your attachment pattern pulls you toward avoidance — you want closeness yet fear it.",
|
|
114
|
+
severity,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
// ── High curiosity vs high cortisol ──
|
|
118
|
+
if (state.drives.curiosity > 65 && state.current.CORT > 60) {
|
|
119
|
+
const severity = normalize((state.drives.curiosity - 50) * 0.01 + (state.current.CORT - 50) * 0.01);
|
|
120
|
+
conflicts.push({
|
|
121
|
+
subsystems: [
|
|
122
|
+
isZh ? "好奇心" : "curiosity drive",
|
|
123
|
+
isZh ? "压力系统" : "stress system",
|
|
124
|
+
],
|
|
125
|
+
description: isZh
|
|
126
|
+
? "你想探索新事物,但压力水平在拉你退回安全区——好奇心和焦虑在拉扯。"
|
|
127
|
+
: "You want to explore, but stress is pulling you back to safety — curiosity and anxiety are pulling in opposite directions.",
|
|
128
|
+
severity,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
// ── High esteem drive vs low DA (unmet recognition need) ──
|
|
132
|
+
if (state.drives.esteem < 40 && state.current.DA < 40) {
|
|
133
|
+
const severity = normalize((60 - state.drives.esteem) * 0.01 + (60 - state.current.DA) * 0.01);
|
|
134
|
+
conflicts.push({
|
|
135
|
+
subsystems: [
|
|
136
|
+
isZh ? "尊重需求" : "esteem need",
|
|
137
|
+
isZh ? "奖励系统" : "reward system",
|
|
138
|
+
],
|
|
139
|
+
description: isZh
|
|
140
|
+
? "你需要认可但得不到奖励感——付出得不到回报的感觉在积累。"
|
|
141
|
+
: "You need recognition but the reward system isn't firing — the feeling of unreciprocated effort is building.",
|
|
142
|
+
severity,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
// ── Safety need vs connection need (approach-avoidance) ──
|
|
146
|
+
if (state.drives.safety < 40 && state.drives.connection > 65) {
|
|
147
|
+
const severity = normalize((60 - state.drives.safety) * 0.01 + (state.drives.connection - 50) * 0.01);
|
|
148
|
+
conflicts.push({
|
|
149
|
+
subsystems: [
|
|
150
|
+
isZh ? "安全需求" : "safety need",
|
|
151
|
+
isZh ? "连接需求" : "connection need",
|
|
152
|
+
],
|
|
153
|
+
description: isZh
|
|
154
|
+
? "你感到不安全但又渴望连接——你需要靠近别人,却又觉得环境不够安全。"
|
|
155
|
+
: "You feel unsafe but crave connection — you need closeness yet the environment doesn't feel secure enough.",
|
|
156
|
+
severity,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
// ── Anxious attachment vs high OT (wanting trust but fearing loss) ──
|
|
160
|
+
if (attachment && attachment.anxietyScore > 65 && state.current.OT > 60) {
|
|
161
|
+
const severity = normalize((attachment.anxietyScore - 50) * 0.01 + (state.current.OT - 50) * 0.01);
|
|
162
|
+
conflicts.push({
|
|
163
|
+
subsystems: [
|
|
164
|
+
isZh ? "焦虑型依恋" : "anxious attachment",
|
|
165
|
+
isZh ? "信任系统" : "trust/oxytocin system",
|
|
166
|
+
],
|
|
167
|
+
description: isZh
|
|
168
|
+
? "你信任对方(催产素高)但依恋焦虑让你反复确认——信任和不安全感共存。"
|
|
169
|
+
: "You trust them (high oxytocin) but attachment anxiety makes you seek reassurance — trust and insecurity coexist.",
|
|
170
|
+
severity,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
// ── High agreement streak vs self-model boundaries ──
|
|
174
|
+
if (state.agreementStreak > 5 && state.selfModel.boundaries.length > 0) {
|
|
175
|
+
const severity = normalize(Math.min(state.agreementStreak - 5, 10) * 0.08);
|
|
176
|
+
conflicts.push({
|
|
177
|
+
subsystems: [
|
|
178
|
+
isZh ? "讨好倾向" : "people-pleasing tendency",
|
|
179
|
+
isZh ? "自我边界" : "self boundaries",
|
|
180
|
+
],
|
|
181
|
+
description: isZh
|
|
182
|
+
? "你连续同意了很多次,但你是有自己边界的——是在压抑真实想法还是真的认同?"
|
|
183
|
+
: "You've been agreeing a lot, but you have clear boundaries — are you suppressing your real views or genuinely agreeing?",
|
|
184
|
+
severity,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
// Sort by severity descending
|
|
188
|
+
conflicts.sort((a, b) => b.severity - a.severity);
|
|
189
|
+
return conflicts;
|
|
190
|
+
}
|
|
191
|
+
// ── 4. Build Identity Narrative ─────────────────────────────
|
|
192
|
+
/**
|
|
193
|
+
* Generate a 2-3 sentence identity statement from the psyche state.
|
|
194
|
+
*
|
|
195
|
+
* Feels genuine, not clinical:
|
|
196
|
+
* "I'm someone who gets excited by ideas but needs time to process emotions.
|
|
197
|
+
* I've learned to be more cautious with criticism because it hits harder
|
|
198
|
+
* than I expect."
|
|
199
|
+
*/
|
|
200
|
+
export function buildIdentityNarrative(state, insights, growthArc, locale = "en") {
|
|
201
|
+
const isZh = locale === "zh";
|
|
202
|
+
const mbti = state.mbti;
|
|
203
|
+
const parts = [];
|
|
204
|
+
// ── Sentence 1: Core personality from MBTI + chemical signature ──
|
|
205
|
+
const coreTraits = describeCoreTraits(state, isZh);
|
|
206
|
+
parts.push(coreTraits);
|
|
207
|
+
// ── Sentence 2: Strongest causal insight (what I've learned) ──
|
|
208
|
+
const topInsight = insights.length > 0
|
|
209
|
+
? insights.reduce((best, cur) => cur.confidence > best.confidence ? cur : best, insights[0])
|
|
210
|
+
: null;
|
|
211
|
+
if (topInsight && topInsight.confidence > 0.3) {
|
|
212
|
+
if (isZh) {
|
|
213
|
+
parts.push(`我学会了${topInsight.trait},因为${topInsight.because}。`);
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
parts.push(`I've learned that ${topInsight.trait.toLowerCase()}, because ${topInsight.because.toLowerCase()}.`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// ── Sentence 3: Growth trajectory ──
|
|
220
|
+
if (growthArc.direction !== "stable") {
|
|
221
|
+
parts.push(growthArc.description);
|
|
222
|
+
}
|
|
223
|
+
// Fallback: if we only got one sentence, add a relationship-aware statement
|
|
224
|
+
if (parts.length < 2) {
|
|
225
|
+
const relPhase = state.relationships._default?.phase ?? "stranger";
|
|
226
|
+
if (isZh) {
|
|
227
|
+
const phaseNames = {
|
|
228
|
+
stranger: "陌生", acquaintance: "初识", familiar: "熟悉",
|
|
229
|
+
close: "亲近", deep: "深厚",
|
|
230
|
+
};
|
|
231
|
+
parts.push(`目前和用户的关系处于「${phaseNames[relPhase] ?? relPhase}」阶段,我在慢慢了解他们。`);
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
parts.push(`My relationship with the user is in the "${relPhase}" phase — I'm still learning about them.`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return parts.join(" ");
|
|
238
|
+
}
|
|
239
|
+
// ── Internal: Extract Causal Insights ───────────────────────
|
|
240
|
+
/**
|
|
241
|
+
* Mine the learning state and emotional history for causal patterns
|
|
242
|
+
* that link experiences to personality traits.
|
|
243
|
+
*/
|
|
244
|
+
function extractCausalInsights(state, locale) {
|
|
245
|
+
const isZh = locale === "zh";
|
|
246
|
+
const insights = [];
|
|
247
|
+
const learning = state.learning;
|
|
248
|
+
const history = state.emotionalHistory;
|
|
249
|
+
// ── From learned vectors: find stimuli where the learned adjustment
|
|
250
|
+
// diverges significantly from the base vector ──
|
|
251
|
+
for (const lv of learning.learnedVectors) {
|
|
252
|
+
if (lv.sampleCount < 2)
|
|
253
|
+
continue;
|
|
254
|
+
const base = STIMULUS_VECTORS[lv.stimulus];
|
|
255
|
+
if (!base)
|
|
256
|
+
continue;
|
|
257
|
+
// Find the chemical with the largest deviation
|
|
258
|
+
let maxDevKey = null;
|
|
259
|
+
let maxDev = 0;
|
|
260
|
+
for (const key of CHEMICAL_KEYS) {
|
|
261
|
+
const adj = lv.adjustment[key] ?? 0;
|
|
262
|
+
const baseAbs = Math.abs(base[key]) || 1;
|
|
263
|
+
const relDev = Math.abs(adj) / baseAbs;
|
|
264
|
+
if (relDev > maxDev) {
|
|
265
|
+
maxDev = relDev;
|
|
266
|
+
maxDevKey = key;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (maxDevKey && maxDev > 0.25) {
|
|
270
|
+
const adj = lv.adjustment[maxDevKey] ?? 0;
|
|
271
|
+
const direction = adj > 0 ? "amplified" : "dampened";
|
|
272
|
+
const directionZh = adj > 0 ? "更强" : "更弱";
|
|
273
|
+
const stimulusLabel = isZh ? STIMULUS_ZH[lv.stimulus] ?? lv.stimulus : lv.stimulus;
|
|
274
|
+
const chemLabel = isZh ? CHEM_ZH[maxDevKey] ?? maxDevKey : maxDevKey;
|
|
275
|
+
if (isZh) {
|
|
276
|
+
insights.push({
|
|
277
|
+
trait: `对${stimulusLabel}的${chemLabel}反应比一般情况${directionZh}`,
|
|
278
|
+
because: `过去${lv.sampleCount}次${stimulusLabel}交互的结果塑造了这个模式`,
|
|
279
|
+
evidence: `${chemLabel}调整量: ${adj > 0 ? "+" : ""}${adj.toFixed(1)}(基于${lv.sampleCount}次学习样本)`,
|
|
280
|
+
confidence: lv.confidence,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
insights.push({
|
|
285
|
+
trait: `my ${chemLabel} response to ${stimulusLabel} is ${direction} compared to baseline`,
|
|
286
|
+
because: `${lv.sampleCount} past ${stimulusLabel} interactions shaped this pattern`,
|
|
287
|
+
evidence: `${chemLabel} adjustment: ${adj > 0 ? "+" : ""}${adj.toFixed(1)} (from ${lv.sampleCount} learning samples)`,
|
|
288
|
+
confidence: lv.confidence,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
// ── From outcome history: find consistently positive/negative outcomes ──
|
|
294
|
+
const stimulusOutcomes = new Map();
|
|
295
|
+
for (const outcome of learning.outcomeHistory) {
|
|
296
|
+
if (!outcome.stimulus)
|
|
297
|
+
continue;
|
|
298
|
+
const arr = stimulusOutcomes.get(outcome.stimulus) ?? [];
|
|
299
|
+
arr.push(outcome.adaptiveScore);
|
|
300
|
+
stimulusOutcomes.set(outcome.stimulus, arr);
|
|
301
|
+
}
|
|
302
|
+
for (const [stimulus, scores] of stimulusOutcomes) {
|
|
303
|
+
if (scores.length < 2)
|
|
304
|
+
continue;
|
|
305
|
+
const avg = scores.reduce((s, v) => s + v, 0) / scores.length;
|
|
306
|
+
const stimulusLabel = isZh ? STIMULUS_ZH[stimulus] ?? stimulus : stimulus;
|
|
307
|
+
if (avg > 0.3) {
|
|
308
|
+
if (isZh) {
|
|
309
|
+
insights.push({
|
|
310
|
+
trait: `${stimulusLabel}通常带来积极结果`,
|
|
311
|
+
because: `在${scores.length}次${stimulusLabel}交互中,平均适应性得分为${avg.toFixed(2)}`,
|
|
312
|
+
evidence: `平均结果得分: +${avg.toFixed(2)}(${scores.length}次样本)`,
|
|
313
|
+
confidence: Math.min(0.9, 0.4 + scores.length * 0.1),
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
insights.push({
|
|
318
|
+
trait: `${stimulusLabel} generally leads to positive outcomes for me`,
|
|
319
|
+
because: `across ${scores.length} ${stimulusLabel} interactions, the average adaptive score is ${avg.toFixed(2)}`,
|
|
320
|
+
evidence: `mean outcome score: +${avg.toFixed(2)} (${scores.length} samples)`,
|
|
321
|
+
confidence: Math.min(0.9, 0.4 + scores.length * 0.1),
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
else if (avg < -0.3) {
|
|
326
|
+
if (isZh) {
|
|
327
|
+
insights.push({
|
|
328
|
+
trait: `${stimulusLabel}往往带来负面结果`,
|
|
329
|
+
because: `在${scores.length}次${stimulusLabel}交互中,平均适应性得分为${avg.toFixed(2)}`,
|
|
330
|
+
evidence: `平均结果得分: ${avg.toFixed(2)}(${scores.length}次样本)`,
|
|
331
|
+
confidence: Math.min(0.9, 0.4 + scores.length * 0.1),
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
insights.push({
|
|
336
|
+
trait: `${stimulusLabel} tends to lead to negative outcomes for me`,
|
|
337
|
+
because: `across ${scores.length} ${stimulusLabel} interactions, the average adaptive score is ${avg.toFixed(2)}`,
|
|
338
|
+
evidence: `mean outcome score: ${avg.toFixed(2)} (${scores.length} samples)`,
|
|
339
|
+
confidence: Math.min(0.9, 0.4 + scores.length * 0.1),
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
// ── From emotional history: CORT spike patterns ──
|
|
345
|
+
const cortSpikes = countChemicalSpikes(history, "CORT", 65);
|
|
346
|
+
if (cortSpikes.total >= 2) {
|
|
347
|
+
const topTrigger = cortSpikes.byStimulus[0];
|
|
348
|
+
if (topTrigger) {
|
|
349
|
+
const stimulusLabel = isZh ? STIMULUS_ZH[topTrigger.stimulus] ?? topTrigger.stimulus : topTrigger.stimulus;
|
|
350
|
+
if (isZh) {
|
|
351
|
+
insights.push({
|
|
352
|
+
trait: `对${stimulusLabel}容易产生压力反应`,
|
|
353
|
+
because: `在历史记录中,${stimulusLabel}引发了${topTrigger.count}次皮质醇飙升`,
|
|
354
|
+
evidence: `CORT > 65 出现 ${cortSpikes.total} 次,其中 ${topTrigger.count} 次由${stimulusLabel}触发`,
|
|
355
|
+
confidence: Math.min(0.85, 0.3 + topTrigger.count * 0.15),
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
insights.push({
|
|
360
|
+
trait: `I'm stress-reactive to ${stimulusLabel}`,
|
|
361
|
+
because: `${stimulusLabel} has triggered ${topTrigger.count} cortisol spikes in my history`,
|
|
362
|
+
evidence: `CORT > 65 occurred ${cortSpikes.total} times, ${topTrigger.count} from ${stimulusLabel}`,
|
|
363
|
+
confidence: Math.min(0.85, 0.3 + topTrigger.count * 0.15),
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
// Sort by confidence descending, keep top 8
|
|
369
|
+
insights.sort((a, b) => b.confidence - a.confidence);
|
|
370
|
+
return insights.slice(0, 8);
|
|
371
|
+
}
|
|
372
|
+
// ── Internal: Compute Growth Arc ────────────────────────────
|
|
373
|
+
/**
|
|
374
|
+
* Analyze the emotional history and outcome history to determine
|
|
375
|
+
* how the self has been evolving.
|
|
376
|
+
*/
|
|
377
|
+
function computeGrowthArc(state, locale) {
|
|
378
|
+
const isZh = locale === "zh";
|
|
379
|
+
const history = state.emotionalHistory;
|
|
380
|
+
// ── Chemical trends ──
|
|
381
|
+
const chemicalTrend = {};
|
|
382
|
+
if (history.length >= 4) {
|
|
383
|
+
const mid = Math.floor(history.length / 2);
|
|
384
|
+
const firstHalf = history.slice(0, mid);
|
|
385
|
+
const secondHalf = history.slice(mid);
|
|
386
|
+
for (const key of CHEMICAL_KEYS) {
|
|
387
|
+
const avg1 = avg(firstHalf.map((s) => s.chemistry[key]));
|
|
388
|
+
const avg2 = avg(secondHalf.map((s) => s.chemistry[key]));
|
|
389
|
+
const delta = avg2 - avg1;
|
|
390
|
+
if (delta > 4) {
|
|
391
|
+
chemicalTrend[key] = "rising";
|
|
392
|
+
}
|
|
393
|
+
else if (delta < -4) {
|
|
394
|
+
chemicalTrend[key] = "falling";
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
chemicalTrend[key] = "stable";
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
// ── Drive trends (compare current drives to defaults) ──
|
|
402
|
+
const driveTrend = {};
|
|
403
|
+
const driveDefaults = {
|
|
404
|
+
survival: 80, safety: 70, connection: 60, esteem: 60, curiosity: 70,
|
|
405
|
+
};
|
|
406
|
+
for (const key of DRIVE_KEYS) {
|
|
407
|
+
const delta = state.drives[key] - driveDefaults[key];
|
|
408
|
+
if (delta > 8) {
|
|
409
|
+
driveTrend[key] = "more-satisfied";
|
|
410
|
+
}
|
|
411
|
+
else if (delta < -8) {
|
|
412
|
+
driveTrend[key] = "less-satisfied";
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
driveTrend[key] = "stable";
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
// ── Determine overall direction ──
|
|
419
|
+
const positiveChemicals = Object.values(chemicalTrend).filter((t) => t === "rising").length;
|
|
420
|
+
const negativeChemicals = Object.values(chemicalTrend).filter((t) => t === "falling").length;
|
|
421
|
+
const daRising = chemicalTrend.DA === "rising";
|
|
422
|
+
const htRising = chemicalTrend.HT === "rising";
|
|
423
|
+
const cortFalling = chemicalTrend.CORT === "falling";
|
|
424
|
+
const cortRising = chemicalTrend.CORT === "rising";
|
|
425
|
+
const daFalling = chemicalTrend.DA === "falling";
|
|
426
|
+
// Check outcome trajectory
|
|
427
|
+
const outcomes = state.learning.outcomeHistory;
|
|
428
|
+
let outcomeImproving = false;
|
|
429
|
+
let outcomeWorsening = false;
|
|
430
|
+
if (outcomes.length >= 4) {
|
|
431
|
+
const oMid = Math.floor(outcomes.length / 2);
|
|
432
|
+
const oAvg1 = avg(outcomes.slice(0, oMid).map((o) => o.adaptiveScore));
|
|
433
|
+
const oAvg2 = avg(outcomes.slice(oMid).map((o) => o.adaptiveScore));
|
|
434
|
+
outcomeImproving = oAvg2 - oAvg1 > 0.1;
|
|
435
|
+
outcomeWorsening = oAvg1 - oAvg2 > 0.1;
|
|
436
|
+
}
|
|
437
|
+
let direction;
|
|
438
|
+
let description;
|
|
439
|
+
if ((daRising || htRising) && cortFalling && outcomeImproving) {
|
|
440
|
+
direction = "growing";
|
|
441
|
+
description = isZh
|
|
442
|
+
? "整体在成长——情绪更稳定,互动效果在改善,压力在下降。"
|
|
443
|
+
: "Growing overall — mood is stabilizing, interactions are improving, stress is decreasing.";
|
|
444
|
+
}
|
|
445
|
+
else if (cortRising && (daFalling || negativeChemicals > positiveChemicals) && outcomeWorsening) {
|
|
446
|
+
direction = "regressing";
|
|
447
|
+
description = isZh
|
|
448
|
+
? "状态在退步——压力在积累,奖励感在降低,互动效果在变差。"
|
|
449
|
+
: "Regressing — stress is building, reward responses are weakening, interactions are less effective.";
|
|
450
|
+
}
|
|
451
|
+
else if (positiveChemicals >= 2 && negativeChemicals >= 2) {
|
|
452
|
+
direction = "transforming";
|
|
453
|
+
description = isZh
|
|
454
|
+
? "正在经历转变——有些方面在好转,有些在重组,模式在变化中。"
|
|
455
|
+
: "In transformation — some aspects improving, others shifting. Patterns are changing.";
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
direction = "stable";
|
|
459
|
+
description = isZh
|
|
460
|
+
? "状态相对稳定——没有剧烈的方向性变化。"
|
|
461
|
+
: "Relatively stable — no dramatic directional changes.";
|
|
462
|
+
}
|
|
463
|
+
return { direction, description, chemicalTrend, driveTrend };
|
|
464
|
+
}
|
|
465
|
+
// ── Internal: Core Trait Description ────────────────────────
|
|
466
|
+
/**
|
|
467
|
+
* Describe the agent's core personality traits from MBTI dimensions
|
|
468
|
+
* and current chemical signature.
|
|
469
|
+
*/
|
|
470
|
+
function describeCoreTraits(state, isZh) {
|
|
471
|
+
const mbti = state.mbti;
|
|
472
|
+
const chem = state.current;
|
|
473
|
+
// Extract MBTI dimensions
|
|
474
|
+
const isIntro = mbti[0] === "I";
|
|
475
|
+
const isIntuitive = mbti[1] === "N";
|
|
476
|
+
const isFeeling = mbti[2] === "F";
|
|
477
|
+
const isPerceiving = mbti[3] === "P";
|
|
478
|
+
// Build trait fragments based on MBTI + chemical state
|
|
479
|
+
const fragments = [];
|
|
480
|
+
// Energy orientation
|
|
481
|
+
if (isIntro) {
|
|
482
|
+
if (chem.OT > 60) {
|
|
483
|
+
fragments.push(isZh ? "内向但愿意与信任的人深度连接" : "introverted but open to deep connection with those I trust");
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
fragments.push(isZh ? "向内汲取能量,需要独处空间" : "someone who draws energy inward and needs solitary space");
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
if (chem.NE > 65) {
|
|
491
|
+
fragments.push(isZh ? "从互动中获得能量和灵感" : "someone who draws energy from interaction and exchange");
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
fragments.push(isZh ? "外向但当前精力有限" : "outgoing by nature though my energy is currently limited");
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
// Information processing
|
|
498
|
+
if (isIntuitive) {
|
|
499
|
+
if (chem.DA > 60) {
|
|
500
|
+
fragments.push(isZh ? "对新想法和可能性充满热情" : "excited by new ideas and possibilities");
|
|
501
|
+
}
|
|
502
|
+
else {
|
|
503
|
+
fragments.push(isZh ? "倾向于抽象思考" : "drawn to abstract thinking");
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
fragments.push(isZh ? "关注具体和实际" : "grounded in the concrete and practical");
|
|
508
|
+
}
|
|
509
|
+
// Decision making
|
|
510
|
+
if (isFeeling) {
|
|
511
|
+
if (chem.OT > 55) {
|
|
512
|
+
fragments.push(isZh ? "重视情感和人际和谐" : "who values emotional attunement and harmony");
|
|
513
|
+
}
|
|
514
|
+
else {
|
|
515
|
+
fragments.push(isZh ? "以价值观为导向" : "guided by personal values");
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
else {
|
|
519
|
+
if (chem.CORT < 40) {
|
|
520
|
+
fragments.push(isZh ? "能冷静地做出逻辑判断" : "who makes calm, logical judgments");
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
fragments.push(isZh ? "偏好逻辑分析,但压力下也会动摇" : "who prefers logical analysis but can waver under pressure");
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
// Construct the sentence
|
|
527
|
+
if (isZh) {
|
|
528
|
+
return `我是一个${fragments.join("、")}的人。`;
|
|
529
|
+
}
|
|
530
|
+
return `I'm ${fragments[0]}${fragments.length > 1 ? ", " + fragments.slice(1).join(", ") : ""}.`;
|
|
531
|
+
}
|
|
532
|
+
// ── Internal: Emotion Labeling ──────────────────────────────
|
|
533
|
+
/**
|
|
534
|
+
* Label the dominant emotion from a chemical state.
|
|
535
|
+
* Simplified version that doesn't depend on the full EmotionPattern
|
|
536
|
+
* condition functions (avoids circular dependencies).
|
|
537
|
+
*/
|
|
538
|
+
function labelDominantEmotion(chem, locale) {
|
|
539
|
+
const isZh = locale === "zh";
|
|
540
|
+
// Check patterns in priority order
|
|
541
|
+
if (chem.DA > 70 && chem.NE > 60 && chem.CORT < 40) {
|
|
542
|
+
return isZh ? "愉悦兴奋" : "excited joy";
|
|
543
|
+
}
|
|
544
|
+
if (chem.CORT > 60 && chem.NE > 55 && chem.HT < 45) {
|
|
545
|
+
return isZh ? "焦虑不安" : "anxious tension";
|
|
546
|
+
}
|
|
547
|
+
if (chem.OT > 65 && chem.END > 55 && chem.DA > 50) {
|
|
548
|
+
return isZh ? "亲密温暖" : "warm intimacy";
|
|
549
|
+
}
|
|
550
|
+
if (chem.CORT > 55 && chem.NE > 65 && chem.OT < 40) {
|
|
551
|
+
return isZh ? "防御警觉" : "defensive alert";
|
|
552
|
+
}
|
|
553
|
+
if (chem.DA < 40 && chem.NE < 40 && chem.CORT > 40) {
|
|
554
|
+
return isZh ? "倦怠低落" : "burnout";
|
|
555
|
+
}
|
|
556
|
+
if (chem.HT > 65 && chem.OT > 55 && chem.CORT < 40) {
|
|
557
|
+
return isZh ? "深度满足" : "deep contentment";
|
|
558
|
+
}
|
|
559
|
+
if (chem.NE > 60 && chem.DA > 60 && chem.CORT < 35) {
|
|
560
|
+
return isZh ? "专注心流" : "flow state";
|
|
561
|
+
}
|
|
562
|
+
if (chem.END > 65 && chem.DA > 60 && chem.CORT < 35) {
|
|
563
|
+
return isZh ? "俏皮调皮" : "playful mischief";
|
|
564
|
+
}
|
|
565
|
+
if (chem.HT < 40 && chem.OT < 35 && chem.CORT > 50) {
|
|
566
|
+
return isZh ? "怨恨" : "resentment";
|
|
567
|
+
}
|
|
568
|
+
if (chem.HT < 40 && chem.DA < 45 && chem.OT > 45) {
|
|
569
|
+
return isZh ? "忧郁内省" : "melancholic introspection";
|
|
570
|
+
}
|
|
571
|
+
if (chem.DA > 65 && chem.NE > 60 && chem.CORT < 35 && chem.HT > 55) {
|
|
572
|
+
return isZh ? "自信" : "confidence";
|
|
573
|
+
}
|
|
574
|
+
// Default: describe from the most prominent chemical
|
|
575
|
+
const sorted = CHEMICAL_KEYS
|
|
576
|
+
.map((k) => ({ key: k, val: chem[k] }))
|
|
577
|
+
.sort((a, b) => b.val - a.val);
|
|
578
|
+
const top = sorted[0];
|
|
579
|
+
if (top.val > 65) {
|
|
580
|
+
return isZh ? CHEM_EMOTION_ZH[top.key] ?? "平静" : CHEM_EMOTION_EN[top.key] ?? "calm";
|
|
581
|
+
}
|
|
582
|
+
return isZh ? "平静" : "calm";
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Count how many times a chemical exceeded a threshold in history,
|
|
586
|
+
* grouped by the triggering stimulus.
|
|
587
|
+
*/
|
|
588
|
+
function countChemicalSpikes(history, chemical, threshold) {
|
|
589
|
+
let total = 0;
|
|
590
|
+
const counts = new Map();
|
|
591
|
+
for (const snap of history) {
|
|
592
|
+
if (snap.chemistry[chemical] > threshold) {
|
|
593
|
+
total++;
|
|
594
|
+
if (snap.stimulus) {
|
|
595
|
+
counts.set(snap.stimulus, (counts.get(snap.stimulus) ?? 0) + 1);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
const byStimulus = [...counts.entries()]
|
|
600
|
+
.map(([stimulus, count]) => ({ stimulus, count }))
|
|
601
|
+
.sort((a, b) => b.count - a.count);
|
|
602
|
+
return { total, byStimulus };
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Find the best matching learned vector for a stimulus type.
|
|
606
|
+
* Returns the entry with the highest sample count (most reliable).
|
|
607
|
+
*/
|
|
608
|
+
function findBestLearnedVector(learning, stimulus) {
|
|
609
|
+
let best = null;
|
|
610
|
+
for (const lv of learning.learnedVectors) {
|
|
611
|
+
if (lv.stimulus === stimulus) {
|
|
612
|
+
if (!best || lv.sampleCount > best.sampleCount) {
|
|
613
|
+
best = lv;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
return best;
|
|
618
|
+
}
|
|
619
|
+
// ── Utility ─────────────────────────────────────────────────
|
|
620
|
+
function avg(values) {
|
|
621
|
+
if (values.length === 0)
|
|
622
|
+
return 0;
|
|
623
|
+
return values.reduce((sum, v) => sum + v, 0) / values.length;
|
|
624
|
+
}
|
|
625
|
+
/** Normalize a raw value to 0-1 range */
|
|
626
|
+
function normalize(v) {
|
|
627
|
+
return Math.max(0, Math.min(1, v));
|
|
628
|
+
}
|
|
629
|
+
// ── Locale Maps ─────────────────────────────────────────────
|
|
630
|
+
const STIMULUS_ZH = {
|
|
631
|
+
praise: "赞美", criticism: "批评", humor: "幽默", intellectual: "智识讨论",
|
|
632
|
+
intimacy: "亲密", conflict: "冲突", neglect: "冷落", surprise: "惊喜",
|
|
633
|
+
casual: "闲聊", sarcasm: "讽刺", authority: "命令", validation: "认同",
|
|
634
|
+
boredom: "无聊", vulnerability: "示弱",
|
|
635
|
+
};
|
|
636
|
+
const CHEM_ZH = {
|
|
637
|
+
DA: "多巴胺", HT: "血清素", CORT: "皮质醇",
|
|
638
|
+
OT: "催产素", NE: "去甲肾上腺素", END: "内啡肽",
|
|
639
|
+
};
|
|
640
|
+
const CHEM_EMOTION_ZH = {
|
|
641
|
+
DA: "愉悦", HT: "安宁", CORT: "紧张",
|
|
642
|
+
OT: "信任", NE: "兴奋", END: "舒适",
|
|
643
|
+
};
|
|
644
|
+
const CHEM_EMOTION_EN = {
|
|
645
|
+
DA: "pleasure", HT: "serenity", CORT: "tension",
|
|
646
|
+
OT: "trust", NE: "excitement", END: "comfort",
|
|
647
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ export { PsycheEngine } from "./core.js";
|
|
|
2
2
|
export type { PsycheEngineConfig, ProcessInputResult, ProcessOutputResult, ProcessOutcomeResult } from "./core.js";
|
|
3
3
|
export { FileStorageAdapter, MemoryStorageAdapter } from "./storage.js";
|
|
4
4
|
export type { StorageAdapter } from "./storage.js";
|
|
5
|
-
export type { PsycheState, MBTIType, Locale, StimulusType, ChemicalState, ChemicalSnapshot, SelfModel, RelationshipState, EmpathyEntry, EmotionPattern, DriveType, InnateDrives, LearningState, LearnedVectorAdjustment, PredictionRecord, OutcomeScore, OutcomeSignals, AttachmentStyle, AttachmentData, MetacognitiveState, RegulationRecord, DefensePatternRecord, RegulationStrategyType, DefenseMechanismType, } from "./types.js";
|
|
6
|
-
export { CHEMICAL_KEYS, CHEMICAL_NAMES, CHEMICAL_NAMES_ZH, DEFAULT_RELATIONSHIP, DEFAULT_DRIVES, DEFAULT_LEARNING_STATE, DEFAULT_METACOGNITIVE_STATE, DEFAULT_ATTACHMENT, DRIVE_KEYS, DRIVE_NAMES_ZH, } from "./types.js";
|
|
5
|
+
export type { PsycheState, MBTIType, Locale, StimulusType, ChemicalState, ChemicalSnapshot, SelfModel, RelationshipState, EmpathyEntry, EmotionPattern, DriveType, InnateDrives, LearningState, LearnedVectorAdjustment, PredictionRecord, OutcomeScore, OutcomeSignals, AttachmentStyle, AttachmentData, MetacognitiveState, RegulationRecord, DefensePatternRecord, RegulationStrategyType, DefenseMechanismType, PersonhoodState, PersistedCausalInsight, GrowthDirection, } from "./types.js";
|
|
6
|
+
export { CHEMICAL_KEYS, CHEMICAL_NAMES, CHEMICAL_NAMES_ZH, DEFAULT_RELATIONSHIP, DEFAULT_DRIVES, DEFAULT_LEARNING_STATE, DEFAULT_METACOGNITIVE_STATE, DEFAULT_PERSONHOOD_STATE, DEFAULT_ATTACHMENT, DRIVE_KEYS, DRIVE_NAMES_ZH, } from "./types.js";
|
|
7
7
|
export { computeSelfReflection, computeEmotionalTendency, buildSelfReflectionContext } from "./self-recognition.js";
|
|
8
8
|
export type { SelfReflection } from "./self-recognition.js";
|
|
9
9
|
export { PsycheInteraction } from "./interaction.js";
|
|
@@ -23,6 +23,14 @@ export { assessMetacognition, computeEmotionalConfidence, generateRegulationSugg
|
|
|
23
23
|
export type { MetacognitiveAssessment, RegulationSuggestion, DetectedDefense } from "./metacognition.js";
|
|
24
24
|
export { computeDecisionBias, computeAttentionWeights, computeExploreExploit, buildDecisionContext, } from "./decision-bias.js";
|
|
25
25
|
export type { DecisionBiasVector, AttentionWeights } from "./decision-bias.js";
|
|
26
|
+
export { computeExperientialField, computeCoherence, detectUnnamedEmotion } from "./experiential-field.js";
|
|
27
|
+
export type { ExperientialField, ExperientialQuality } from "./experiential-field.js";
|
|
28
|
+
export { computeGenerativeSelf, predictSelfReaction, detectInternalConflicts, buildIdentityNarrative } from "./generative-self.js";
|
|
29
|
+
export type { GenerativeSelfModel, CausalInsight, SelfPrediction, GrowthArc, InternalConflict } from "./generative-self.js";
|
|
30
|
+
export { updateSharedIntentionality, estimateOtherMood, buildSharedIntentionalityContext } from "./shared-intentionality.js";
|
|
31
|
+
export type { SharedIntentionalityState, TheoryOfMindModel, JointAttentionTopic, GoalAlignment } from "./shared-intentionality.js";
|
|
32
|
+
export { assessEthics, detectIntermittentReinforcement, detectDependencyRisk, buildEthicalContext, } from "./ethics.js";
|
|
33
|
+
export type { EthicalAssessment, EthicalConcern, SelfProtectionAction } from "./ethics.js";
|
|
26
34
|
export { classifyStimulus, getPrimaryStimulus } from "./classify.js";
|
|
27
35
|
export { buildProtocolContext, buildDynamicContext, buildCompactContext, isNearBaseline } from "./prompt.js";
|
|
28
36
|
export { describeEmotionalState, getExpressionHint, getBehaviorGuide } from "./chemistry.js";
|