psyche-ai 4.0.0 → 5.0.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/dist/index.js CHANGED
@@ -12,7 +12,7 @@
12
12
  export { PsycheEngine } from "./core.js";
13
13
  // Storage
14
14
  export { FileStorageAdapter, MemoryStorageAdapter } from "./storage.js";
15
- 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";
15
+ 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";
16
16
  // Self-recognition
17
17
  export { computeSelfReflection, computeEmotionalTendency, buildSelfReflectionContext } from "./self-recognition.js";
18
18
  // Multi-agent interaction
@@ -33,6 +33,14 @@ export { updateAttachment, computeSeparationEffect, computeReunionEffect, } from
33
33
  export { assessMetacognition, computeEmotionalConfidence, generateRegulationSuggestions, detectDefenseMechanisms, } from "./metacognition.js";
34
34
  // Decision bias (P5)
35
35
  export { computeDecisionBias, computeAttentionWeights, computeExploreExploit, buildDecisionContext, } from "./decision-bias.js";
36
+ // Experiential field (P6)
37
+ export { computeExperientialField, computeCoherence, detectUnnamedEmotion } from "./experiential-field.js";
38
+ // Generative self (P6)
39
+ export { computeGenerativeSelf, predictSelfReaction, detectInternalConflicts, buildIdentityNarrative } from "./generative-self.js";
40
+ // Shared intentionality (P6)
41
+ export { updateSharedIntentionality, estimateOtherMood, buildSharedIntentionalityContext } from "./shared-intentionality.js";
42
+ // Emotional ethics (P6)
43
+ export { assessEthics, detectIntermittentReinforcement, detectDependencyRisk, buildEthicalContext, } from "./ethics.js";
36
44
  // Utilities — for custom adapter / advanced use
37
45
  export { classifyStimulus, getPrimaryStimulus } from "./classify.js";
38
46
  export { buildProtocolContext, buildDynamicContext, buildCompactContext, isNearBaseline } from "./prompt.js";
@@ -4,7 +4,7 @@
4
4
  // ============================================================
5
5
  import { readFile, writeFile, access, rename, constants } from "node:fs/promises";
6
6
  import { join } from "node:path";
7
- import { CHEMICAL_KEYS, CHEMICAL_NAMES, CHEMICAL_NAMES_ZH, DEFAULT_RELATIONSHIP, DEFAULT_DRIVES, DEFAULT_LEARNING_STATE, DEFAULT_METACOGNITIVE_STATE, MAX_EMOTIONAL_HISTORY, MAX_RELATIONSHIP_MEMORY, } from "./types.js";
7
+ import { CHEMICAL_KEYS, CHEMICAL_NAMES, CHEMICAL_NAMES_ZH, DEFAULT_RELATIONSHIP, DEFAULT_DRIVES, DEFAULT_LEARNING_STATE, DEFAULT_METACOGNITIVE_STATE, DEFAULT_PERSONHOOD_STATE, MAX_EMOTIONAL_HISTORY, MAX_RELATIONSHIP_MEMORY, } from "./types.js";
8
8
  import { getBaseline, getDefaultSelfModel, extractMBTI, getSensitivity, getTemperament } from "./profiles.js";
9
9
  import { applyDecay, detectEmotions } from "./chemistry.js";
10
10
  import { decayDrives, computeEffectiveBaseline } from "./drives.js";
@@ -242,12 +242,14 @@ export function migrateToLatest(raw, fallbackName) {
242
242
  // v2→v3: add drives
243
243
  // v3→v4: add learning
244
244
  // v4→v5: add metacognition
245
+ // v5→v6: add personhood
245
246
  return {
246
247
  ...state,
247
- version: 5,
248
+ version: 6,
248
249
  drives: state.drives ?? { ...DEFAULT_DRIVES },
249
250
  learning: state.learning ?? { ...DEFAULT_LEARNING_STATE },
250
251
  metacognition: state.metacognition ?? { ...DEFAULT_METACOGNITIVE_STATE },
252
+ personhood: state.personhood ?? { ...DEFAULT_PERSONHOOD_STATE },
251
253
  };
252
254
  }
253
255
  /**
@@ -261,7 +263,7 @@ export async function initializeState(workspaceDir, opts, logger = NOOP_LOGGER)
261
263
  const selfModel = getDefaultSelfModel(mbti);
262
264
  const now = new Date().toISOString();
263
265
  const state = {
264
- version: 5,
266
+ version: 6,
265
267
  mbti,
266
268
  baseline,
267
269
  current: { ...baseline },
@@ -277,6 +279,7 @@ export async function initializeState(workspaceDir, opts, logger = NOOP_LOGGER)
277
279
  lastDisagreement: null,
278
280
  learning: { ...DEFAULT_LEARNING_STATE },
279
281
  metacognition: { ...DEFAULT_METACOGNITIVE_STATE },
282
+ personhood: { ...DEFAULT_PERSONHOOD_STATE },
280
283
  meta: {
281
284
  agentName,
282
285
  createdAt: now,
@@ -0,0 +1,72 @@
1
+ import type { PsycheState, StimulusType, RelationshipState, Locale } from "./types.js";
2
+ /** Estimated mood of the other party */
3
+ export type EstimatedMood = "positive" | "negative" | "neutral" | "mixed";
4
+ /** Estimated intent of the other party */
5
+ export type EstimatedIntent = "collaborative" | "adversarial" | "disengaged" | "exploratory" | "support-seeking";
6
+ /** Simplified model of the other's mental state */
7
+ export interface TheoryOfMindModel {
8
+ estimatedMood: EstimatedMood;
9
+ estimatedIntent: EstimatedIntent;
10
+ confidence: number;
11
+ lastUpdated: string;
12
+ }
13
+ /** What we're jointly focused on */
14
+ export interface JointAttentionTopic {
15
+ topic: string;
16
+ initiator: "self" | "other";
17
+ turnsSustained: number;
18
+ engagement: number;
19
+ }
20
+ /** Are we pulling in the same direction? */
21
+ export interface GoalAlignment {
22
+ aligned: boolean;
23
+ divergence: number;
24
+ description: string;
25
+ }
26
+ /** Top-level shared intentionality state */
27
+ export interface SharedIntentionalityState {
28
+ /** Current joint attention topic, null if no shared focus */
29
+ jointAttention: JointAttentionTopic | null;
30
+ /** Goal alignment: are we working toward the same thing? */
31
+ goalAlignment: GoalAlignment;
32
+ /** Theory of mind: model of the other's mental state */
33
+ theoryOfMind: TheoryOfMindModel;
34
+ /** Mutual awareness: does the other know I'm attending to them? (0-1) */
35
+ mutualAwareness: number;
36
+ }
37
+ export declare const DEFAULT_THEORY_OF_MIND: TheoryOfMindModel;
38
+ export declare const DEFAULT_GOAL_ALIGNMENT: GoalAlignment;
39
+ export declare const DEFAULT_SHARED_INTENTIONALITY: SharedIntentionalityState;
40
+ /**
41
+ * Infer the other's emotional state from the detected stimulus.
42
+ *
43
+ * Uses stimulus type as the primary signal, calibrated by
44
+ * relationship history. Longer, deeper relationships yield
45
+ * higher confidence — you read familiar people better.
46
+ */
47
+ export declare function estimateOtherMood(stimulus: StimulusType | null, relationship: RelationshipState, previous?: TheoryOfMindModel): {
48
+ mood: EstimatedMood;
49
+ confidence: number;
50
+ };
51
+ /**
52
+ * Main update function for shared intentionality.
53
+ *
54
+ * Takes the agent's psyche state, a detected stimulus from the other,
55
+ * and optionally the previous shared intentionality state.
56
+ * Returns the updated state with refreshed theory of mind,
57
+ * joint attention, goal alignment, and mutual awareness.
58
+ *
59
+ * @param psyche Current PsycheState of this agent
60
+ * @param stimulus Detected stimulus type from the other's message (null if none)
61
+ * @param userId User/agent ID for relationship lookup
62
+ * @param previous Previous SharedIntentionalityState (null on first turn)
63
+ */
64
+ export declare function updateSharedIntentionality(psyche: PsycheState, stimulus: StimulusType | null, userId?: string, previous?: SharedIntentionalityState | null): SharedIntentionalityState;
65
+ /**
66
+ * Build a compact prompt-injectable context string for shared intentionality.
67
+ *
68
+ * Returns empty string when there's nothing meaningful to report
69
+ * (low confidence, no joint attention, early interaction).
70
+ * Only injects when the shared state carries useful signal.
71
+ */
72
+ export declare function buildSharedIntentionalityContext(state: SharedIntentionalityState, locale: Locale): string;
@@ -0,0 +1,486 @@
1
+ // ============================================================
2
+ // Shared Intentionality (共享意向性) — Joint Attention & Theory of Mind
3
+ //
4
+ // P6: Digital Personhood. Goes beyond empathy (feeling what the
5
+ // other feels) into joint attention — knowing that we're both
6
+ // thinking about the same thing, and knowing that the other
7
+ // knows this.
8
+ //
9
+ // Pure computation, zero LLM calls, zero dependencies.
10
+ // 1. Theory of Mind — simplified model of the other's mental state
11
+ // 2. Joint Attention — detecting sustained shared focus
12
+ // 3. Goal Alignment — are we pulling in the same direction?
13
+ // 4. Mutual Awareness — does the other know I'm attending to them?
14
+ // ============================================================
15
+ // ── Defaults ─────────────────────────────────────────────────
16
+ export const DEFAULT_THEORY_OF_MIND = {
17
+ estimatedMood: "neutral",
18
+ estimatedIntent: "exploratory",
19
+ confidence: 0.2,
20
+ lastUpdated: new Date().toISOString(),
21
+ };
22
+ export const DEFAULT_GOAL_ALIGNMENT = {
23
+ aligned: false,
24
+ divergence: 0.5,
25
+ description: "No goal alignment established yet.",
26
+ };
27
+ export const DEFAULT_SHARED_INTENTIONALITY = {
28
+ jointAttention: null,
29
+ goalAlignment: { ...DEFAULT_GOAL_ALIGNMENT },
30
+ theoryOfMind: { ...DEFAULT_THEORY_OF_MIND },
31
+ mutualAwareness: 0,
32
+ };
33
+ // ── Stimulus → Mood Mapping ──────────────────────────────────
34
+ /** Maps stimulus types to their most likely mood signal from the other */
35
+ const STIMULUS_MOOD_MAP = {
36
+ praise: "positive",
37
+ validation: "positive",
38
+ intimacy: "positive",
39
+ humor: "positive",
40
+ surprise: "positive",
41
+ casual: "neutral",
42
+ intellectual: "neutral",
43
+ vulnerability: "mixed",
44
+ sarcasm: "negative",
45
+ criticism: "negative",
46
+ authority: "negative",
47
+ conflict: "negative",
48
+ neglect: "negative",
49
+ boredom: "negative",
50
+ };
51
+ /** Maps stimulus types to their most likely intent signal */
52
+ const STIMULUS_INTENT_MAP = {
53
+ praise: "collaborative",
54
+ validation: "collaborative",
55
+ intimacy: "collaborative",
56
+ humor: "collaborative",
57
+ intellectual: "exploratory",
58
+ surprise: "exploratory",
59
+ casual: "disengaged",
60
+ boredom: "disengaged",
61
+ neglect: "disengaged",
62
+ vulnerability: "support-seeking",
63
+ sarcasm: "adversarial",
64
+ criticism: "adversarial",
65
+ conflict: "adversarial",
66
+ authority: "adversarial",
67
+ };
68
+ /** Stimuli that signal the other is actively engaged with us */
69
+ const HIGH_ENGAGEMENT_STIMULI = new Set([
70
+ "praise", "validation", "intimacy", "intellectual",
71
+ "humor", "vulnerability", "conflict", "criticism",
72
+ ]);
73
+ /** Stimuli that signal the other is pulling away */
74
+ const LOW_ENGAGEMENT_STIMULI = new Set([
75
+ "neglect", "boredom", "casual",
76
+ ]);
77
+ // ── EMA smoothing factor ─────────────────────────────────────
78
+ const EMA_ALPHA = 0.3;
79
+ // ── 1. estimateOtherMood ─────────────────────────────────────
80
+ /**
81
+ * Infer the other's emotional state from the detected stimulus.
82
+ *
83
+ * Uses stimulus type as the primary signal, calibrated by
84
+ * relationship history. Longer, deeper relationships yield
85
+ * higher confidence — you read familiar people better.
86
+ */
87
+ export function estimateOtherMood(stimulus, relationship, previous) {
88
+ // No stimulus — hold previous estimate with decaying confidence
89
+ if (stimulus === null) {
90
+ if (previous) {
91
+ return {
92
+ mood: previous.estimatedMood,
93
+ confidence: Math.max(0.1, previous.confidence * 0.85),
94
+ };
95
+ }
96
+ return { mood: "neutral", confidence: 0.15 };
97
+ }
98
+ const rawMood = STIMULUS_MOOD_MAP[stimulus];
99
+ // Confidence from relationship depth: strangers → low, deep → high
100
+ const phaseConfidence = {
101
+ stranger: 0.25,
102
+ acquaintance: 0.40,
103
+ familiar: 0.60,
104
+ close: 0.75,
105
+ deep: 0.90,
106
+ };
107
+ const depthConfidence = phaseConfidence[relationship.phase] ?? 0.30;
108
+ // Trust calibrates confidence — low trust means we're less sure
109
+ // of our read on them
110
+ const trustFactor = relationship.trust / 100;
111
+ // Combine: depth provides the ceiling, trust scales within it
112
+ let confidence = depthConfidence * (0.5 + 0.5 * trustFactor);
113
+ // If previous estimate exists and mood matches, confidence rises (consistency)
114
+ if (previous && previous.estimatedMood === rawMood) {
115
+ confidence = Math.min(1, confidence + 0.1);
116
+ }
117
+ // Mixed mood detection: if stimulus contradicts previous estimate,
118
+ // the truth might be "mixed"
119
+ let finalMood = rawMood;
120
+ if (previous && previous.confidence > 0.4) {
121
+ const prevPositive = previous.estimatedMood === "positive";
122
+ const nowNegative = rawMood === "negative";
123
+ const prevNegative = previous.estimatedMood === "negative";
124
+ const nowPositive = rawMood === "positive";
125
+ if ((prevPositive && nowNegative) || (prevNegative && nowPositive)) {
126
+ finalMood = "mixed";
127
+ confidence *= 0.8; // contradiction lowers confidence
128
+ }
129
+ }
130
+ return { mood: finalMood, confidence: clamp01(confidence) };
131
+ }
132
+ // ── 2. updateTheoryOfMind ────────────────────────────────────
133
+ /**
134
+ * Update the theory of mind model based on a new stimulus and
135
+ * relationship context.
136
+ */
137
+ function updateTheoryOfMind(stimulus, relationship, previous) {
138
+ const { mood, confidence: moodConfidence } = estimateOtherMood(stimulus, relationship, previous);
139
+ // Intent estimation
140
+ let estimatedIntent;
141
+ if (stimulus === null) {
142
+ estimatedIntent = previous?.estimatedIntent ?? "exploratory";
143
+ }
144
+ else {
145
+ const rawIntent = STIMULUS_INTENT_MAP[stimulus];
146
+ // EMA between previous intent and new signal when previous exists
147
+ if (previous && previous.estimatedIntent === rawIntent) {
148
+ estimatedIntent = rawIntent; // reinforced
149
+ }
150
+ else {
151
+ estimatedIntent = rawIntent; // new signal takes precedence
152
+ }
153
+ }
154
+ // Overall confidence: blend mood confidence with previous
155
+ let confidence = moodConfidence;
156
+ if (previous) {
157
+ confidence = previous.confidence * (1 - EMA_ALPHA) + moodConfidence * EMA_ALPHA;
158
+ }
159
+ return {
160
+ estimatedMood: mood,
161
+ estimatedIntent,
162
+ confidence: clamp01(confidence),
163
+ lastUpdated: new Date().toISOString(),
164
+ };
165
+ }
166
+ // ── 3. detectJointAttention ──────────────────────────────────
167
+ /**
168
+ * Detect or sustain joint attention based on stimulus continuity.
169
+ *
170
+ * Joint attention emerges when:
171
+ * - The same category of stimulus persists across turns
172
+ * - Both parties are engaged (high-engagement stimuli)
173
+ *
174
+ * Collapses when:
175
+ * - Low-engagement stimulus appears (boredom, neglect)
176
+ * - No stimulus detected for a turn
177
+ */
178
+ function detectJointAttention(stimulus, previous) {
179
+ // No stimulus — attention fades
180
+ if (stimulus === null) {
181
+ if (previous && previous.turnsSustained > 1) {
182
+ // Slow fade: reduce engagement, keep topic alive for one grace turn
183
+ return {
184
+ ...previous,
185
+ engagement: Math.max(0, previous.engagement - 0.3),
186
+ };
187
+ }
188
+ return null;
189
+ }
190
+ // Low engagement stimulus breaks joint attention
191
+ if (LOW_ENGAGEMENT_STIMULI.has(stimulus)) {
192
+ return null;
193
+ }
194
+ // Map stimulus to a topic category for continuity detection
195
+ const topicCategory = stimulusToTopicCategory(stimulus);
196
+ // Check if this continues the previous topic
197
+ if (previous) {
198
+ const previousCategory = stimulusToTopicCategory(previous.topic);
199
+ const sameTopic = previousCategory === topicCategory
200
+ || previous.topic === topicCategory;
201
+ if (sameTopic) {
202
+ // Sustain: increment turns, boost engagement
203
+ const isHighEngagement = HIGH_ENGAGEMENT_STIMULI.has(stimulus);
204
+ const engagementDelta = isHighEngagement ? 0.15 : 0.05;
205
+ return {
206
+ topic: topicCategory,
207
+ initiator: previous.initiator,
208
+ turnsSustained: previous.turnsSustained + 1,
209
+ engagement: clamp01(previous.engagement + engagementDelta),
210
+ };
211
+ }
212
+ }
213
+ // New topic — only establish if it's a high-engagement stimulus
214
+ if (HIGH_ENGAGEMENT_STIMULI.has(stimulus)) {
215
+ return {
216
+ topic: topicCategory,
217
+ initiator: "other", // stimulus comes from the other party
218
+ turnsSustained: 1,
219
+ engagement: 0.4, // initial engagement is moderate
220
+ };
221
+ }
222
+ return null;
223
+ }
224
+ /**
225
+ * Map a stimulus type to a broader topic category for continuity tracking.
226
+ * Multiple stimuli can map to the same topic to allow topic persistence
227
+ * across slight stimulus variation.
228
+ */
229
+ function stimulusToTopicCategory(stimulus) {
230
+ const TOPIC_MAP = {
231
+ praise: "affirmation",
232
+ validation: "affirmation",
233
+ intimacy: "connection",
234
+ vulnerability: "connection",
235
+ humor: "play",
236
+ surprise: "play",
237
+ intellectual: "exploration",
238
+ criticism: "tension",
239
+ conflict: "tension",
240
+ sarcasm: "tension",
241
+ authority: "tension",
242
+ casual: "casual",
243
+ neglect: "disengagement",
244
+ boredom: "disengagement",
245
+ };
246
+ return TOPIC_MAP[stimulus] ?? stimulus;
247
+ }
248
+ // ── 4. assessGoalAlignment ───────────────────────────────────
249
+ /**
250
+ * Assess whether the agent and the other party are working toward
251
+ * the same thing, based on the theory of mind and joint attention.
252
+ */
253
+ function assessGoalAlignment(theoryOfMind, jointAttention, relationship) {
254
+ const intent = theoryOfMind.estimatedIntent;
255
+ const mood = theoryOfMind.estimatedMood;
256
+ // Base divergence from intent
257
+ const intentDivergence = {
258
+ collaborative: 0.1,
259
+ exploratory: 0.3,
260
+ "support-seeking": 0.2,
261
+ disengaged: 0.7,
262
+ adversarial: 0.9,
263
+ };
264
+ let divergence = intentDivergence[intent];
265
+ // Joint attention reduces divergence — shared focus implies shared direction
266
+ if (jointAttention && jointAttention.engagement > 0.3) {
267
+ const attentionBonus = jointAttention.engagement * 0.3;
268
+ divergence = Math.max(0, divergence - attentionBonus);
269
+ }
270
+ // Trust-based correction: high trust → we assume more alignment
271
+ const trustCorrection = (relationship.trust - 50) / 200; // -0.25 to +0.25
272
+ divergence = clamp01(divergence - trustCorrection);
273
+ // Mood correction: negative mood increases perceived divergence slightly
274
+ if (mood === "negative") {
275
+ divergence = clamp01(divergence + 0.1);
276
+ }
277
+ else if (mood === "positive") {
278
+ divergence = clamp01(divergence - 0.05);
279
+ }
280
+ const aligned = divergence < 0.4;
281
+ const description = buildAlignmentDescription(aligned, divergence, intent, jointAttention);
282
+ return { aligned, divergence, description };
283
+ }
284
+ function buildAlignmentDescription(aligned, divergence, intent, jointAttention) {
285
+ if (aligned && jointAttention && jointAttention.engagement > 0.5) {
286
+ return `Goals aligned — jointly engaged in ${jointAttention.topic}.`;
287
+ }
288
+ if (aligned) {
289
+ return "Goals roughly aligned, moving in the same direction.";
290
+ }
291
+ if (divergence > 0.7) {
292
+ const reason = intent === "adversarial"
293
+ ? "The other party seems oppositional."
294
+ : intent === "disengaged"
295
+ ? "The other party seems disengaged."
296
+ : "Significant goal divergence detected.";
297
+ return reason;
298
+ }
299
+ return "Goals partially misaligned — some divergence in direction.";
300
+ }
301
+ // ── 5. computeMutualAwareness ────────────────────────────────
302
+ /**
303
+ * Estimate mutual awareness: does the other know I'm attending to them?
304
+ *
305
+ * This emerges from:
306
+ * - Sustained joint attention (they keep engaging on the same topic)
307
+ * - High-engagement stimuli (they're clearly directing attention at us)
308
+ * - Relationship depth (deeper relationships have higher baseline awareness)
309
+ */
310
+ function computeMutualAwareness(stimulus, jointAttention, relationship, previous) {
311
+ // Baseline from relationship depth
312
+ const phaseAwareness = {
313
+ stranger: 0.1,
314
+ acquaintance: 0.2,
315
+ familiar: 0.35,
316
+ close: 0.5,
317
+ deep: 0.65,
318
+ };
319
+ const baseline = phaseAwareness[relationship.phase] ?? 0.15;
320
+ let awareness = previous;
321
+ // High-engagement stimulus → they're clearly aware of us
322
+ if (stimulus && HIGH_ENGAGEMENT_STIMULI.has(stimulus)) {
323
+ awareness = awareness * (1 - EMA_ALPHA) + 0.8 * EMA_ALPHA;
324
+ }
325
+ else if (stimulus && LOW_ENGAGEMENT_STIMULI.has(stimulus)) {
326
+ awareness = awareness * (1 - EMA_ALPHA) + 0.1 * EMA_ALPHA;
327
+ }
328
+ // Joint attention boost — sustained shared focus implies mutual awareness
329
+ if (jointAttention && jointAttention.turnsSustained > 2) {
330
+ const jointBoost = Math.min(0.3, jointAttention.engagement * 0.3);
331
+ awareness = Math.min(1, awareness + jointBoost);
332
+ }
333
+ // Decay toward baseline when no strong signal
334
+ if (stimulus === null) {
335
+ awareness = awareness * 0.85 + baseline * 0.15;
336
+ }
337
+ // Floor at baseline — relationship depth guarantees minimum awareness
338
+ awareness = Math.max(baseline, awareness);
339
+ return clamp01(awareness);
340
+ }
341
+ // ── 6. updateSharedIntentionality (main) ─────────────────────
342
+ /**
343
+ * Main update function for shared intentionality.
344
+ *
345
+ * Takes the agent's psyche state, a detected stimulus from the other,
346
+ * and optionally the previous shared intentionality state.
347
+ * Returns the updated state with refreshed theory of mind,
348
+ * joint attention, goal alignment, and mutual awareness.
349
+ *
350
+ * @param psyche Current PsycheState of this agent
351
+ * @param stimulus Detected stimulus type from the other's message (null if none)
352
+ * @param userId User/agent ID for relationship lookup
353
+ * @param previous Previous SharedIntentionalityState (null on first turn)
354
+ */
355
+ export function updateSharedIntentionality(psyche, stimulus, userId, previous) {
356
+ const relKey = userId ?? "_default";
357
+ const relationship = psyche.relationships[relKey]
358
+ ?? { trust: 50, intimacy: 30, phase: "acquaintance" };
359
+ const prev = previous ?? null;
360
+ // 1. Update theory of mind
361
+ const theoryOfMind = updateTheoryOfMind(stimulus, relationship, prev?.theoryOfMind);
362
+ // 2. Detect / sustain joint attention
363
+ const jointAttention = detectJointAttention(stimulus, prev?.jointAttention ?? null);
364
+ // 3. Assess goal alignment
365
+ const goalAlignment = assessGoalAlignment(theoryOfMind, jointAttention, relationship);
366
+ // 4. Compute mutual awareness
367
+ const mutualAwareness = computeMutualAwareness(stimulus, jointAttention, relationship, prev?.mutualAwareness ?? 0);
368
+ return {
369
+ jointAttention,
370
+ goalAlignment,
371
+ theoryOfMind,
372
+ mutualAwareness,
373
+ };
374
+ }
375
+ // ── 7. buildSharedIntentionalityContext ───────────────────────
376
+ /**
377
+ * Build a compact prompt-injectable context string for shared intentionality.
378
+ *
379
+ * Returns empty string when there's nothing meaningful to report
380
+ * (low confidence, no joint attention, early interaction).
381
+ * Only injects when the shared state carries useful signal.
382
+ */
383
+ export function buildSharedIntentionalityContext(state, locale) {
384
+ const isZh = locale === "zh";
385
+ const lines = [];
386
+ const { theoryOfMind, jointAttention, goalAlignment, mutualAwareness } = state;
387
+ // Gate: skip injection when confidence is too low to be useful
388
+ const hasJointAttention = jointAttention !== null && jointAttention.engagement > 0.3;
389
+ const hasConfidentToM = theoryOfMind.confidence > 0.35;
390
+ const hasMeaningfulAwareness = mutualAwareness > 0.3;
391
+ if (!hasJointAttention && !hasConfidentToM && !hasMeaningfulAwareness) {
392
+ return "";
393
+ }
394
+ const title = isZh ? "共享意向" : "Shared intentionality";
395
+ lines.push(`[${title}]`);
396
+ // Joint attention
397
+ if (hasJointAttention && jointAttention) {
398
+ const topicName = isZh
399
+ ? TOPIC_NAMES_ZH[jointAttention.topic] ?? jointAttention.topic
400
+ : jointAttention.topic;
401
+ if (jointAttention.turnsSustained > 2 && jointAttention.engagement > 0.5) {
402
+ lines.push(isZh
403
+ ? `你们都沉浸在「${topicName}」的话题中(${jointAttention.turnsSustained}轮)。`
404
+ : `You're both absorbed in ${topicName} (${jointAttention.turnsSustained} turns).`);
405
+ }
406
+ else {
407
+ lines.push(isZh
408
+ ? `你们似乎都在关注「${topicName}」。`
409
+ : `You both seem focused on ${topicName}.`);
410
+ }
411
+ }
412
+ // Theory of mind — what we sense about the other
413
+ if (hasConfidentToM) {
414
+ const intentDesc = isZh
415
+ ? INTENT_NAMES_ZH[theoryOfMind.estimatedIntent]
416
+ : INTENT_NAMES_EN[theoryOfMind.estimatedIntent];
417
+ const moodDesc = isZh
418
+ ? MOOD_NAMES_ZH[theoryOfMind.estimatedMood]
419
+ : MOOD_NAMES_EN[theoryOfMind.estimatedMood];
420
+ lines.push(isZh
421
+ ? `你感觉对方${moodDesc},似乎想要${intentDesc}。`
422
+ : `You sense they're ${moodDesc} and want to ${intentDesc}.`);
423
+ }
424
+ // Goal alignment — only when there's a clear signal
425
+ if (goalAlignment.aligned && goalAlignment.divergence < 0.3) {
426
+ lines.push(isZh
427
+ ? "你们的目标方向一致。"
428
+ : "Your goals are aligned.");
429
+ }
430
+ else if (!goalAlignment.aligned && goalAlignment.divergence > 0.6) {
431
+ lines.push(isZh
432
+ ? "你感到你们的方向有些分歧。"
433
+ : "You sense some divergence in direction.");
434
+ }
435
+ // Mutual awareness — only at high levels
436
+ if (mutualAwareness > 0.6) {
437
+ lines.push(isZh
438
+ ? "对方清楚地意识到你在关注他们。"
439
+ : "The other is clearly aware of your attention.");
440
+ }
441
+ // If we only got the title, nothing was worth injecting
442
+ if (lines.length <= 1) {
443
+ return "";
444
+ }
445
+ return lines.join("\n");
446
+ }
447
+ // ── i18n helpers ─────────────────────────────────────────────
448
+ const TOPIC_NAMES_ZH = {
449
+ affirmation: "肯定与认可",
450
+ connection: "情感连接",
451
+ play: "轻松与玩笑",
452
+ exploration: "探索与讨论",
453
+ tension: "紧张与对立",
454
+ casual: "日常闲聊",
455
+ disengagement: "疏离",
456
+ };
457
+ const INTENT_NAMES_ZH = {
458
+ collaborative: "合作",
459
+ adversarial: "对抗",
460
+ disengaged: "脱离对话",
461
+ exploratory: "探索",
462
+ "support-seeking": "寻求支持",
463
+ };
464
+ const INTENT_NAMES_EN = {
465
+ collaborative: "collaborate",
466
+ adversarial: "push back",
467
+ disengaged: "disengage",
468
+ exploratory: "explore",
469
+ "support-seeking": "seek support",
470
+ };
471
+ const MOOD_NAMES_ZH = {
472
+ positive: "心情不错",
473
+ negative: "情绪低落",
474
+ neutral: "状态平稳",
475
+ mixed: "心情复杂",
476
+ };
477
+ const MOOD_NAMES_EN = {
478
+ positive: "in a good mood",
479
+ negative: "in a low mood",
480
+ neutral: "neutral",
481
+ mixed: "in a mixed state",
482
+ };
483
+ // ── Utility ──────────────────────────────────────────────────
484
+ function clamp01(value) {
485
+ return Math.max(0, Math.min(1, value));
486
+ }
package/dist/types.d.ts CHANGED
@@ -175,9 +175,43 @@ export interface MetacognitiveState {
175
175
  }
176
176
  /** Default empty metacognitive state */
177
177
  export declare const DEFAULT_METACOGNITIVE_STATE: MetacognitiveState;
178
- /** Persisted psyche state for an agent (v5: metacognition + decision modulation) */
178
+ /** Max causal insights to persist */
179
+ export declare const MAX_CAUSAL_INSIGHTS = 20;
180
+ /** Max ethical concern history entries */
181
+ export declare const MAX_ETHICAL_HISTORY = 15;
182
+ /** Persisted causal insight about self */
183
+ export interface PersistedCausalInsight {
184
+ trait: string;
185
+ because: string;
186
+ confidence: number;
187
+ discoveredAt: string;
188
+ }
189
+ /** Growth direction */
190
+ export type GrowthDirection = "growing" | "stable" | "regressing" | "transforming";
191
+ /** Persisted personhood state */
192
+ export interface PersonhoodState {
193
+ causalInsights: PersistedCausalInsight[];
194
+ growthDirection: GrowthDirection;
195
+ identityNarrative: string;
196
+ /** Ethical concerns detected over time */
197
+ ethicalConcernHistory: {
198
+ type: string;
199
+ severity: number;
200
+ timestamp: string;
201
+ }[];
202
+ /** Theory of mind per-user */
203
+ theoryOfMind: Record<string, {
204
+ estimatedMood: string;
205
+ estimatedIntent: string;
206
+ confidence: number;
207
+ lastUpdated: string;
208
+ }>;
209
+ }
210
+ /** Default empty personhood state */
211
+ export declare const DEFAULT_PERSONHOOD_STATE: PersonhoodState;
212
+ /** Persisted psyche state for an agent (v6: digital personhood) */
179
213
  export interface PsycheState {
180
- version: 3 | 4 | 5;
214
+ version: 3 | 4 | 5 | 6;
181
215
  mbti: MBTIType;
182
216
  baseline: ChemicalState;
183
217
  current: ChemicalState;
@@ -191,6 +225,7 @@ export interface PsycheState {
191
225
  lastDisagreement: string | null;
192
226
  learning: LearningState;
193
227
  metacognition: MetacognitiveState;
228
+ personhood: PersonhoodState;
194
229
  meta: {
195
230
  agentName: string;
196
231
  createdAt: string;