psyche-ai 10.2.4 → 11.2.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 +49 -60
- package/dist/adapters/claude-sdk.d.ts +5 -5
- package/dist/adapters/claude-sdk.js +34 -32
- package/dist/adapters/mcp.js +4 -4
- package/dist/adapters/openclaw.js +12 -14
- package/dist/attachment.d.ts +3 -13
- package/dist/attachment.js +36 -88
- package/dist/autonomic.d.ts +4 -4
- package/dist/autonomic.js +28 -24
- package/dist/chemistry.d.ts +47 -21
- package/dist/chemistry.js +145 -91
- package/dist/circadian.d.ts +11 -43
- package/dist/circadian.js +24 -84
- package/dist/cli.js +33 -27
- package/dist/context-classifier.js +2 -2
- package/dist/core.d.ts +3 -3
- package/dist/core.js +99 -88
- package/dist/custom-profile.d.ts +20 -20
- package/dist/custom-profile.js +12 -12
- package/dist/decision-bias.d.ts +7 -8
- package/dist/decision-bias.js +74 -74
- package/dist/demo.js +5 -5
- package/dist/diagnostics.d.ts +6 -6
- package/dist/diagnostics.js +22 -22
- package/dist/drives.d.ts +15 -47
- package/dist/drives.js +98 -196
- package/dist/ethics.d.ts +3 -3
- package/dist/ethics.js +23 -23
- package/dist/experience.d.ts +34 -0
- package/dist/experience.js +200 -0
- package/dist/experiential-field.d.ts +19 -14
- package/dist/experiential-field.js +110 -100
- package/dist/generative-self.d.ts +5 -5
- package/dist/generative-self.js +124 -115
- package/dist/guards.d.ts +4 -4
- package/dist/guards.js +7 -7
- package/dist/i18n.js +61 -61
- package/dist/index.d.ts +8 -2
- package/dist/index.js +8 -1
- package/dist/input-turn.js +4 -6
- package/dist/interaction.d.ts +4 -4
- package/dist/interaction.js +10 -10
- package/dist/learning.d.ts +6 -6
- package/dist/learning.js +18 -18
- package/dist/metacognition.d.ts +2 -2
- package/dist/metacognition.js +79 -94
- package/dist/perceive.d.ts +44 -0
- package/dist/perceive.js +231 -0
- package/dist/primary-systems.d.ts +2 -2
- package/dist/primary-systems.js +21 -19
- package/dist/profiles.d.ts +5 -13
- package/dist/profiles.js +33 -51
- package/dist/prompt.d.ts +2 -2
- package/dist/prompt.js +51 -53
- package/dist/psyche-file.d.ts +7 -7
- package/dist/psyche-file.js +77 -78
- package/dist/relation-dynamics.d.ts +4 -0
- package/dist/relation-dynamics.js +1 -1
- package/dist/reply-envelope.d.ts +25 -1
- package/dist/reply-envelope.js +26 -11
- package/dist/self-recognition.d.ts +3 -3
- package/dist/self-recognition.js +17 -17
- package/dist/subjectivity.js +7 -7
- package/dist/temporal.d.ts +6 -6
- package/dist/temporal.js +37 -39
- package/dist/types.d.ts +67 -45
- package/dist/types.js +55 -51
- package/package.json +1 -1
package/dist/metacognition.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
//
|
|
13
13
|
// Zero dependencies. Pure heuristic/statistical. No LLM calls.
|
|
14
14
|
// ============================================================
|
|
15
|
-
import {
|
|
15
|
+
import { DIMENSION_KEYS, DIMENSION_NAMES, DIMENSION_SPECS, MAX_REGULATION_HISTORY, MAX_DEFENSE_PATTERNS } from "./types.js";
|
|
16
16
|
// ── Constants ────────────────────────────────────────────────
|
|
17
17
|
/** Stimulus types that are emotionally negative (stress-inducing) */
|
|
18
18
|
const NEGATIVE_STIMULI = new Set([
|
|
@@ -32,39 +32,31 @@ const MIN_HISTORY_FOR_CONFIDENCE = 3;
|
|
|
32
32
|
const MAX_SOOTHING_ADJUSTMENT = 5;
|
|
33
33
|
/** Maximum chemistry micro-adjustment for reappraisal */
|
|
34
34
|
const MAX_REAPPRAISAL_ADJUSTMENT = 8;
|
|
35
|
-
function
|
|
36
|
-
const spec =
|
|
35
|
+
function formatDimensionWindow(key, state) {
|
|
36
|
+
const spec = DIMENSION_SPECS[key];
|
|
37
37
|
const current = Math.round(state.current[key]);
|
|
38
38
|
const baseline = Math.round(state.baseline[key]);
|
|
39
39
|
const delta = Math.round(state.current[key] - state.baseline[key]);
|
|
40
|
-
return `${
|
|
40
|
+
return `${DIMENSION_NAMES[key]} ${current} (baseline ${baseline}, Δ${delta >= 0 ? "+" : ""}${delta}, normal ${spec.normalMin}-${spec.normalMax}, half-life ~${spec.halfLifeHours.toFixed(1)}h)`;
|
|
41
41
|
}
|
|
42
42
|
function buildRegulationAction(key, _state, direction) {
|
|
43
43
|
switch (key) {
|
|
44
|
-
case "
|
|
44
|
+
case "order":
|
|
45
45
|
return direction === "elevated"
|
|
46
|
-
? "Next 3 turns:
|
|
47
|
-
: "Next 3 turns:
|
|
48
|
-
case "
|
|
49
|
-
return direction === "elevated"
|
|
50
|
-
? "Next 3 turns: reduce intimacy push by half, avoid nicknames or extra reassurance, keep warmth neutral."
|
|
51
|
-
: "Next 3 turns: do not force closeness; keep warmth gentle but wait for the user to move closer first.";
|
|
52
|
-
case "NE":
|
|
46
|
+
? "Next 3 turns: keep composure, but do not over-smooth or pretend everything is settled."
|
|
47
|
+
: "Next 3 turns: lower tone intensity, facts before stance, and avoid absolutist wording.";
|
|
48
|
+
case "flow":
|
|
53
49
|
return direction === "elevated"
|
|
54
50
|
? "Next 3 turns: halve initiative, stay on one topic, and avoid energetic jumps or rapid escalation."
|
|
55
|
-
: "Next 3 turns: keep replies
|
|
56
|
-
case "
|
|
57
|
-
return direction === "elevated"
|
|
58
|
-
? "Next 3 turns: cut playful expansion and stay task-anchored; do not over-volunteer or overshare."
|
|
59
|
-
: "Next 3 turns: keep replies purposeful and avoid sounding flat or disengaged.";
|
|
60
|
-
case "END":
|
|
51
|
+
: "Next 3 turns: keep replies purposeful and focused rather than drifting or going blank.";
|
|
52
|
+
case "boundary":
|
|
61
53
|
return direction === "elevated"
|
|
62
|
-
? "Next 3 turns:
|
|
63
|
-
: "Next 3 turns:
|
|
64
|
-
case "
|
|
54
|
+
? "Next 3 turns: soften the boundary slightly; allow one step closer without over-disclosure."
|
|
55
|
+
: "Next 3 turns: reinforce clarity of self/non-self; do not merge perspectives prematurely.";
|
|
56
|
+
case "resonance":
|
|
65
57
|
return direction === "elevated"
|
|
66
|
-
? "Next 3 turns:
|
|
67
|
-
: "Next 3 turns:
|
|
58
|
+
? "Next 3 turns: reduce intimacy push by half, avoid nicknames or extra reassurance, keep warmth neutral."
|
|
59
|
+
: "Next 3 turns: do not force closeness; keep warmth gentle but wait for the user to move closer first.";
|
|
68
60
|
default:
|
|
69
61
|
return "Next 3 turns: keep expression closer to baseline and avoid amplifying the current deviation.";
|
|
70
62
|
}
|
|
@@ -103,7 +95,7 @@ function evaluateRegulationFeedback(state, emotionalConfidence) {
|
|
|
103
95
|
function formatRegulationFeedback(feedback) {
|
|
104
96
|
const metricLabel = feedback.targetMetric === "emotional-confidence"
|
|
105
97
|
? "emotional confidence"
|
|
106
|
-
:
|
|
98
|
+
: DIMENSION_NAMES[feedback.targetMetric];
|
|
107
99
|
const gapBefore = feedback.targetMetric === "emotional-confidence"
|
|
108
100
|
? `${(feedback.gapBefore * 100).toFixed(0)}%`
|
|
109
101
|
: `${Math.round(feedback.gapBefore)}`;
|
|
@@ -194,11 +186,11 @@ export function computeEmotionalConfidence(state, currentStimulus, recentOutcome
|
|
|
194
186
|
*/
|
|
195
187
|
function computeExtremityPenalty(state) {
|
|
196
188
|
let totalDeviation = 0;
|
|
197
|
-
for (const key of
|
|
189
|
+
for (const key of DIMENSION_KEYS) {
|
|
198
190
|
totalDeviation += Math.abs(state.current[key] - state.baseline[key]);
|
|
199
191
|
}
|
|
200
|
-
// Average deviation across
|
|
201
|
-
const avgDeviation = totalDeviation /
|
|
192
|
+
// Average deviation across 4 dimensions, max possible = 100 each
|
|
193
|
+
const avgDeviation = totalDeviation / DIMENSION_KEYS.length;
|
|
202
194
|
// Scale: deviation of 30+ gives meaningful penalty, max penalty = 0.25
|
|
203
195
|
return Math.min(0.25, Math.max(0, (avgDeviation - 10) / 80) * 0.25);
|
|
204
196
|
}
|
|
@@ -263,7 +255,7 @@ function attemptCognitiveReappraisal(state, currentStimulus, emotionalConfidence
|
|
|
263
255
|
// Suggest chemistry adjustment: pull extreme values toward moderate range
|
|
264
256
|
const adjustment = {};
|
|
265
257
|
let hasAdjustment = false;
|
|
266
|
-
for (const key of
|
|
258
|
+
for (const key of DIMENSION_KEYS) {
|
|
267
259
|
const deviation = state.current[key] - state.baseline[key];
|
|
268
260
|
if (Math.abs(deviation) > MODERATE_DEVIATION_THRESHOLD) {
|
|
269
261
|
// Suggest moving partway back toward baseline
|
|
@@ -291,78 +283,78 @@ function attemptCognitiveReappraisal(state, currentStimulus, emotionalConfidence
|
|
|
291
283
|
* StrategicExpression — choose an expression different from raw chemistry.
|
|
292
284
|
*
|
|
293
285
|
* Triggers when: raw chemistry would produce a contextually inappropriate
|
|
294
|
-
* expression. E.g.,
|
|
295
|
-
* or high
|
|
286
|
+
* expression. E.g., low order but the relationship context calls for warmth,
|
|
287
|
+
* or high flow but the user is being vulnerable and needs seriousness.
|
|
296
288
|
*/
|
|
297
289
|
function attemptStrategicExpression(state, currentStimulus, emotionalConfidence) {
|
|
298
290
|
const { current, baseline } = state;
|
|
299
|
-
// Case 1:
|
|
300
|
-
if (current.
|
|
291
|
+
// Case 1: Low order (high stress/entropy) but user is being vulnerable — express warmth not defensiveness
|
|
292
|
+
if (current.order < baseline.order - MODERATE_DEVIATION_THRESHOLD
|
|
301
293
|
&& currentStimulus === "vulnerability") {
|
|
302
294
|
return {
|
|
303
295
|
strategy: "strategic-expression",
|
|
304
|
-
description: "
|
|
296
|
+
description: "Internal order is low (stressed/entropic), but the user is showing vulnerability. Express warmth and openness rather than defensiveness.",
|
|
305
297
|
action: "Next 3 turns: keep warmth available, answer the vulnerability directly, and avoid defensive sharpness.",
|
|
306
298
|
horizonTurns: 3,
|
|
307
|
-
targetMetric: "
|
|
308
|
-
targetValue: baseline.
|
|
309
|
-
gapBefore: Math.abs(current.
|
|
299
|
+
targetMetric: "order",
|
|
300
|
+
targetValue: baseline.order,
|
|
301
|
+
gapBefore: Math.abs(current.order - baseline.order),
|
|
310
302
|
chemistryAdjustment: {
|
|
311
|
-
|
|
312
|
-
|
|
303
|
+
resonance: Math.min(MAX_REAPPRAISAL_ADJUSTMENT, (baseline.order - current.order) * 0.2),
|
|
304
|
+
order: Math.min(MAX_REAPPRAISAL_ADJUSTMENT, (baseline.order - current.order) * 0.15),
|
|
313
305
|
},
|
|
314
306
|
confidence: 0.75,
|
|
315
307
|
};
|
|
316
308
|
}
|
|
317
|
-
// Case 2: High
|
|
318
|
-
if (current.
|
|
309
|
+
// Case 2: High flow (over-energized) but user needs seriousness — tone down
|
|
310
|
+
if (current.flow > baseline.flow + MODERATE_DEVIATION_THRESHOLD
|
|
319
311
|
&& (currentStimulus === "conflict" || currentStimulus === "criticism")) {
|
|
320
312
|
return {
|
|
321
313
|
strategy: "strategic-expression",
|
|
322
|
-
description: "
|
|
314
|
+
description: "Flow is high (energized/playful), but the context calls for seriousness. Moderate the energy without suppressing it entirely.",
|
|
323
315
|
action: "Next 2 turns: keep humor secondary, answer the actual issue first, and do not deflect with playfulness.",
|
|
324
316
|
horizonTurns: 2,
|
|
325
|
-
targetMetric: "
|
|
326
|
-
targetValue: baseline.
|
|
327
|
-
gapBefore: Math.abs(current.
|
|
317
|
+
targetMetric: "flow",
|
|
318
|
+
targetValue: baseline.flow,
|
|
319
|
+
gapBefore: Math.abs(current.flow - baseline.flow),
|
|
328
320
|
chemistryAdjustment: {
|
|
329
|
-
|
|
321
|
+
flow: -Math.min(MAX_REAPPRAISAL_ADJUSTMENT, (current.flow - baseline.flow) * 0.25),
|
|
330
322
|
},
|
|
331
323
|
confidence: 0.7,
|
|
332
324
|
};
|
|
333
325
|
}
|
|
334
|
-
// Case 3: Low
|
|
335
|
-
if (current.
|
|
326
|
+
// Case 3: Low resonance (distant) but user is being intimate — lean in instead of pulling away
|
|
327
|
+
if (current.resonance < baseline.resonance - MODERATE_DEVIATION_THRESHOLD
|
|
336
328
|
&& currentStimulus === "intimacy") {
|
|
337
329
|
return {
|
|
338
330
|
strategy: "strategic-expression",
|
|
339
|
-
description: "
|
|
331
|
+
description: "Resonance is low (feeling distant), but the user is reaching out for closeness. Consider allowing the connection rather than following the withdrawal impulse.",
|
|
340
332
|
action: "Next 3 turns: allow one step of warmth, but do not overcorrect into clinginess or over-disclosure.",
|
|
341
333
|
horizonTurns: 3,
|
|
342
|
-
targetMetric: "
|
|
343
|
-
targetValue: baseline.
|
|
344
|
-
gapBefore: Math.abs(current.
|
|
334
|
+
targetMetric: "resonance",
|
|
335
|
+
targetValue: baseline.resonance,
|
|
336
|
+
gapBefore: Math.abs(current.resonance - baseline.resonance),
|
|
345
337
|
chemistryAdjustment: {
|
|
346
|
-
|
|
338
|
+
resonance: Math.min(MAX_REAPPRAISAL_ADJUSTMENT, (baseline.resonance - current.resonance) * 0.2),
|
|
347
339
|
},
|
|
348
340
|
confidence: 0.65,
|
|
349
341
|
};
|
|
350
342
|
}
|
|
351
|
-
// Case 4:
|
|
352
|
-
if (current.
|
|
353
|
-
&& current.
|
|
343
|
+
// Case 4: Low order + high flow (defensive/reactive) but stimulus is just casual — overreacting
|
|
344
|
+
if (current.order < baseline.order - MODERATE_DEVIATION_THRESHOLD
|
|
345
|
+
&& current.flow > baseline.flow + MODERATE_DEVIATION_THRESHOLD
|
|
354
346
|
&& (currentStimulus === "casual" || currentStimulus === "humor")) {
|
|
355
347
|
return {
|
|
356
348
|
strategy: "strategic-expression",
|
|
357
|
-
description: "
|
|
349
|
+
description: "Self-state is in a reactive pattern (low order, high flow), but the interaction is benign. The intensity is disproportionate to the stimulus.",
|
|
358
350
|
action: "Next 2 turns: soften the edge, stay literal, and avoid reading threat into a neutral interaction.",
|
|
359
351
|
horizonTurns: 2,
|
|
360
|
-
targetMetric: "
|
|
361
|
-
targetValue: baseline.
|
|
362
|
-
gapBefore: Math.abs(current.
|
|
352
|
+
targetMetric: "order",
|
|
353
|
+
targetValue: baseline.order,
|
|
354
|
+
gapBefore: Math.abs(current.order - baseline.order),
|
|
363
355
|
chemistryAdjustment: {
|
|
364
|
-
|
|
365
|
-
|
|
356
|
+
order: Math.min(MAX_REAPPRAISAL_ADJUSTMENT, (baseline.order - current.order) * 0.2),
|
|
357
|
+
flow: -Math.min(MAX_REAPPRAISAL_ADJUSTMENT, (current.flow - baseline.flow) * 0.15),
|
|
366
358
|
},
|
|
367
359
|
confidence: 0.6,
|
|
368
360
|
};
|
|
@@ -394,8 +386,8 @@ function attemptSelfSoothing(state) {
|
|
|
394
386
|
const adjustment = {};
|
|
395
387
|
let hasAdjustment = false;
|
|
396
388
|
let maxDeviation = 0;
|
|
397
|
-
let mostDeviatedKey = "
|
|
398
|
-
for (const key of
|
|
389
|
+
let mostDeviatedKey = "order";
|
|
390
|
+
for (const key of DIMENSION_KEYS) {
|
|
399
391
|
const deviation = current[key] - baseline[key];
|
|
400
392
|
if (Math.abs(deviation) > EXTREME_DEVIATION_THRESHOLD) {
|
|
401
393
|
// Gentle pull toward baseline: 10% of deviation, clamped
|
|
@@ -411,11 +403,11 @@ function attemptSelfSoothing(state) {
|
|
|
411
403
|
if (!hasAdjustment)
|
|
412
404
|
return null;
|
|
413
405
|
const direction = current[mostDeviatedKey] > baseline[mostDeviatedKey] ? "elevated" : "depleted";
|
|
414
|
-
const
|
|
415
|
-
const window =
|
|
406
|
+
const dimName = DIMENSION_NAMES[mostDeviatedKey];
|
|
407
|
+
const window = formatDimensionWindow(mostDeviatedKey, state);
|
|
416
408
|
return {
|
|
417
409
|
strategy: "self-soothing",
|
|
418
|
-
description: `${
|
|
410
|
+
description: `${dimName} is significantly ${direction}. ${window}.`,
|
|
419
411
|
action: buildRegulationAction(mostDeviatedKey, state, direction),
|
|
420
412
|
horizonTurns: 3,
|
|
421
413
|
targetMetric: mostDeviatedKey,
|
|
@@ -488,7 +480,7 @@ function detectRationalization(state, recentOutcomes) {
|
|
|
488
480
|
/**
|
|
489
481
|
* Projection — attributing own emotional state to the user.
|
|
490
482
|
*
|
|
491
|
-
* Pattern: agent has extreme
|
|
483
|
+
* Pattern: agent has extreme state (especially low order or low resonance)
|
|
492
484
|
* and the empathy log shows attributing negative emotions to the user
|
|
493
485
|
* that don't match the stimulus.
|
|
494
486
|
*/
|
|
@@ -497,9 +489,9 @@ function detectProjection(state, currentStimulus) {
|
|
|
497
489
|
// Need empathy data and significant self-distress
|
|
498
490
|
if (!empathyLog)
|
|
499
491
|
return null;
|
|
500
|
-
const
|
|
501
|
-
const
|
|
502
|
-
const selfDistress = Math.max(
|
|
492
|
+
const orderDrop = baseline.order - current.order; // low order = more distressed
|
|
493
|
+
const boundaryDrop = baseline.boundary - current.boundary; // low boundary = more distressed
|
|
494
|
+
const selfDistress = Math.max(orderDrop, boundaryDrop);
|
|
503
495
|
if (selfDistress < MODERATE_DEVIATION_THRESHOLD)
|
|
504
496
|
return null;
|
|
505
497
|
// Check if the stimulus is neutral/positive but the agent perceived
|
|
@@ -510,17 +502,17 @@ function detectProjection(state, currentStimulus) {
|
|
|
510
502
|
if (stimulusIsPositive && perceivedUserNegative) {
|
|
511
503
|
return {
|
|
512
504
|
mechanism: "projection",
|
|
513
|
-
evidence: `High internal distress (
|
|
505
|
+
evidence: `High internal distress (order deviation: ${Math.round(-orderDrop)}) while perceiving user as "${empathyLog.userState}" despite "${currentStimulus}" stimulus. Own distress may be coloring perception.`,
|
|
514
506
|
strength: clamp01(selfDistress / 40),
|
|
515
507
|
};
|
|
516
508
|
}
|
|
517
|
-
// Also check: agent's
|
|
518
|
-
if (current.
|
|
509
|
+
// Also check: agent's flow elevated + empathy mismatch
|
|
510
|
+
if (current.flow > baseline.flow + MODERATE_DEVIATION_THRESHOLD
|
|
519
511
|
&& empathyLog.resonance === "mismatch") {
|
|
520
512
|
return {
|
|
521
513
|
mechanism: "projection",
|
|
522
|
-
evidence: `Elevated
|
|
523
|
-
strength: clamp01((current.
|
|
514
|
+
evidence: `Elevated flow (deviation: +${Math.round(current.flow - baseline.flow)}) with empathy mismatch. The heightened state may be distorting emotional reading of the user.`,
|
|
515
|
+
strength: clamp01((current.flow - baseline.flow) / 40),
|
|
524
516
|
};
|
|
525
517
|
}
|
|
526
518
|
return null;
|
|
@@ -528,23 +520,23 @@ function detectProjection(state, currentStimulus) {
|
|
|
528
520
|
/**
|
|
529
521
|
* Sublimation — redirecting drive energy to constructive output.
|
|
530
522
|
*
|
|
531
|
-
* Pattern: high
|
|
532
|
-
* (low
|
|
523
|
+
* Pattern: high flow combined with blocked connection drives
|
|
524
|
+
* (low resonance, low intimacy), channeled into intellectual or creative engagement.
|
|
533
525
|
* This is a HEALTHY defense — surface it as a positive self-awareness note.
|
|
534
526
|
*/
|
|
535
527
|
function detectSublimation(state, currentStimulus) {
|
|
536
528
|
const { current, baseline, drives } = state;
|
|
537
529
|
// High energy but low connection
|
|
538
|
-
const highEnergy = current.
|
|
539
|
-
const lowConnection = drives.connection < 45 || current.
|
|
530
|
+
const highEnergy = current.flow > baseline.flow + 10;
|
|
531
|
+
const lowConnection = drives.connection < 45 || current.resonance < baseline.resonance - 10;
|
|
540
532
|
if (!highEnergy || !lowConnection)
|
|
541
533
|
return null;
|
|
542
534
|
// Being channeled into intellectual/constructive activity
|
|
543
535
|
if (currentStimulus === "intellectual" || currentStimulus === "casual") {
|
|
544
|
-
const energyLevel =
|
|
536
|
+
const energyLevel = current.flow - baseline.flow;
|
|
545
537
|
return {
|
|
546
538
|
mechanism: "sublimation",
|
|
547
|
-
evidence: `High activation energy (
|
|
539
|
+
evidence: `High activation energy (flow elevated) with unmet connection needs being channeled into ${currentStimulus} engagement. This is adaptive redirection.`,
|
|
548
540
|
strength: clamp01(energyLevel / 30),
|
|
549
541
|
};
|
|
550
542
|
}
|
|
@@ -553,15 +545,15 @@ function detectSublimation(state, currentStimulus) {
|
|
|
553
545
|
/**
|
|
554
546
|
* Avoidance — withdrawing from stimuli associated with past negative outcomes.
|
|
555
547
|
*
|
|
556
|
-
* Pattern: the agent is in a withdrawn state (low
|
|
548
|
+
* Pattern: the agent is in a withdrawn state (low flow, low resonance) when facing
|
|
557
549
|
* a stimulus type that has historically caused negative outcomes. The emotional
|
|
558
550
|
* system is pre-emptively shutting down engagement.
|
|
559
551
|
*/
|
|
560
552
|
function detectAvoidance(state, currentStimulus, recentOutcomes) {
|
|
561
553
|
const { current, baseline } = state;
|
|
562
554
|
// Check for withdrawn state: low engagement markers
|
|
563
|
-
const isWithdrawn = current.
|
|
564
|
-
&& current.
|
|
555
|
+
const isWithdrawn = current.flow < baseline.flow - 10
|
|
556
|
+
&& current.resonance < baseline.resonance - 10;
|
|
565
557
|
if (!isWithdrawn)
|
|
566
558
|
return null;
|
|
567
559
|
// Check if this stimulus type has negative outcome history
|
|
@@ -572,10 +564,10 @@ function detectAvoidance(state, currentStimulus, recentOutcomes) {
|
|
|
572
564
|
/ stimulusOutcomes.length;
|
|
573
565
|
if (avgScore >= -0.15)
|
|
574
566
|
return null; // not negative enough
|
|
575
|
-
const withdrawalStrength = (Math.abs(current.
|
|
567
|
+
const withdrawalStrength = (Math.abs(current.flow - baseline.flow) + Math.abs(current.resonance - baseline.resonance)) / 2;
|
|
576
568
|
return {
|
|
577
569
|
mechanism: "avoidance",
|
|
578
|
-
evidence: `Withdrawal pattern detected (
|
|
570
|
+
evidence: `Withdrawal pattern detected (flow/resonance below baseline) in response to "${currentStimulus}", which has averaged ${avgScore.toFixed(2)} outcome score. The emotional system may be pre-emptively disengaging.`,
|
|
579
571
|
strength: clamp01(withdrawalStrength / 25 * Math.abs(avgScore)),
|
|
580
572
|
};
|
|
581
573
|
}
|
|
@@ -713,14 +705,7 @@ export function updateMetacognitiveState(metacognition, assessment) {
|
|
|
713
705
|
};
|
|
714
706
|
}
|
|
715
707
|
// ── Display Labels ───────────────────────────────────────────
|
|
716
|
-
|
|
717
|
-
DA: "Dopamine",
|
|
718
|
-
HT: "Serotonin",
|
|
719
|
-
CORT: "Cortisol",
|
|
720
|
-
OT: "Oxytocin",
|
|
721
|
-
NE: "Norepinephrine",
|
|
722
|
-
END: "Endorphins",
|
|
723
|
-
};
|
|
708
|
+
// Display names are now provided by DIMENSION_NAMES from types.ts
|
|
724
709
|
const STRATEGY_LABELS = {
|
|
725
710
|
"reappraisal": "Cognitive reappraisal",
|
|
726
711
|
"strategic-expression": "Strategic expression",
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { SelfState, StimulusType, AppraisalAxes, PsycheMode, ClassificationResult, InnateDrives, Locale } from "./types.js";
|
|
2
|
+
import type { TraitDriftState } from "./types.js";
|
|
3
|
+
/** Everything the perceiver needs to know about the self */
|
|
4
|
+
export interface Self {
|
|
5
|
+
current: SelfState;
|
|
6
|
+
baseline: SelfState;
|
|
7
|
+
sensitivity: number;
|
|
8
|
+
personalityIntensity: number;
|
|
9
|
+
mode: PsycheMode;
|
|
10
|
+
maxDimensionDelta: number;
|
|
11
|
+
drives: InnateDrives;
|
|
12
|
+
previousAppraisal?: AppraisalAxes;
|
|
13
|
+
trust?: number;
|
|
14
|
+
recentStimuli?: (StimulusType | null)[];
|
|
15
|
+
traitDrift?: TraitDriftState;
|
|
16
|
+
stateHistory?: Array<{
|
|
17
|
+
stimulus: StimulusType | null;
|
|
18
|
+
}>;
|
|
19
|
+
locale?: Locale;
|
|
20
|
+
/** External classification (from pluggable classifier + LLM fallback).
|
|
21
|
+
* When provided, perception uses these as raw signal instead of
|
|
22
|
+
* running the built-in classifier. */
|
|
23
|
+
rawClassifications?: ClassificationResult[];
|
|
24
|
+
}
|
|
25
|
+
/** The result of perceiving. Self-state is already changed. */
|
|
26
|
+
export interface Perception {
|
|
27
|
+
/** New self-state. This IS the feeling. */
|
|
28
|
+
state: SelfState;
|
|
29
|
+
/** Appraisal axes — what this input means to me existentially */
|
|
30
|
+
appraisal: AppraisalAxes;
|
|
31
|
+
/** Post-hoc annotation: which stimulus category best describes
|
|
32
|
+
* what I just felt. For logging, learning, drives — not a driver. */
|
|
33
|
+
dominantStimulus: StimulusType | null;
|
|
34
|
+
/** Confidence in the perception */
|
|
35
|
+
confidence: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Perceive an input through the self.
|
|
39
|
+
*
|
|
40
|
+
* This is the only function external code needs. The same text
|
|
41
|
+
* through different selves produces different state changes.
|
|
42
|
+
* That IS having a self.
|
|
43
|
+
*/
|
|
44
|
+
export declare function perceive(text: string, self: Self): Perception;
|
package/dist/perceive.js
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// perceive — the atomic act of subjective perception
|
|
3
|
+
//
|
|
4
|
+
// v11: perceive through 4D self-state, not 6 chemicals.
|
|
5
|
+
//
|
|
6
|
+
// Input: text + self state
|
|
7
|
+
// Output: new self-state + appraisal + annotation
|
|
8
|
+
//
|
|
9
|
+
// The self-state delta IS the appraisal. When you perceive
|
|
10
|
+
// something that threatens your boundary, boundary drops.
|
|
11
|
+
// That IS the appraisal — not a separate computation.
|
|
12
|
+
// ============================================================
|
|
13
|
+
import { DIMENSION_KEYS } from "./types.js";
|
|
14
|
+
import { classifyStimulus } from "./classify.js";
|
|
15
|
+
import { computeAppraisalAxes } from "./appraisal.js";
|
|
16
|
+
import { STIMULUS_VECTORS, clamp } from "./chemistry.js";
|
|
17
|
+
import { computeEffectiveSensitivity } from "./drives.js";
|
|
18
|
+
import { MODE_PROFILES } from "./types.js";
|
|
19
|
+
// ── The act of perception ───────────────────────────────────
|
|
20
|
+
/**
|
|
21
|
+
* Perceive an input through the self.
|
|
22
|
+
*
|
|
23
|
+
* This is the only function external code needs. The same text
|
|
24
|
+
* through different selves produces different state changes.
|
|
25
|
+
* That IS having a self.
|
|
26
|
+
*/
|
|
27
|
+
export function perceive(text, self) {
|
|
28
|
+
if (!text.trim()) {
|
|
29
|
+
return {
|
|
30
|
+
state: { ...self.current },
|
|
31
|
+
appraisal: computeAppraisalAxes("", {
|
|
32
|
+
mode: self.mode,
|
|
33
|
+
previous: self.previousAppraisal,
|
|
34
|
+
}),
|
|
35
|
+
dominantStimulus: null,
|
|
36
|
+
confidence: 0,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// ── Raw signal: what the words say ────────────────────────
|
|
40
|
+
const raw = self.rawClassifications
|
|
41
|
+
?? classifyStimulus(text, self.recentStimuli);
|
|
42
|
+
// ── Appraisal: what the words mean to me ──────────────────
|
|
43
|
+
const appraisal = computeAppraisalAxes(text, {
|
|
44
|
+
mode: self.mode,
|
|
45
|
+
previous: self.previousAppraisal,
|
|
46
|
+
});
|
|
47
|
+
// If nothing detected, return unchanged state
|
|
48
|
+
if (raw.length === 0 || raw[0].confidence < 0.5) {
|
|
49
|
+
enrichAppraisal(appraisal, null, 0);
|
|
50
|
+
return {
|
|
51
|
+
state: { ...self.current },
|
|
52
|
+
appraisal,
|
|
53
|
+
dominantStimulus: null,
|
|
54
|
+
confidence: raw[0]?.confidence ?? 0,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// ── Subjective modulation: my state colors my reading ─────
|
|
58
|
+
const modulated = modulate(raw, appraisal, self);
|
|
59
|
+
// ── Self-state change: feel it ────────────────────────────
|
|
60
|
+
const modeProfile = MODE_PROFILES[self.mode];
|
|
61
|
+
const modeMultiplier = modeProfile.dynamicsMultiplier;
|
|
62
|
+
const maxDelta = modeProfile.maxDimensionDelta ?? self.maxDimensionDelta;
|
|
63
|
+
const confidenceIntensity = 0.6 + (raw[0].confidence - 0.5) * 1.2;
|
|
64
|
+
const dominant = modulated[0];
|
|
65
|
+
const baseSensitivity = computeEffectiveSensitivity(self.sensitivity, self.current, self.baseline, dominant.type, self.traitDrift);
|
|
66
|
+
const totalSensitivity = baseSensitivity * self.personalityIntensity * modeMultiplier * confidenceIntensity;
|
|
67
|
+
const state = feel(self.current, modulated, totalSensitivity, maxDelta, self);
|
|
68
|
+
// ── Annotate: what did I just feel? ───────────────────────
|
|
69
|
+
enrichAppraisal(appraisal, dominant.type, dominant.weight);
|
|
70
|
+
return {
|
|
71
|
+
state,
|
|
72
|
+
appraisal,
|
|
73
|
+
dominantStimulus: dominant.type,
|
|
74
|
+
confidence: raw[0].confidence,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// ── Subjective modulation ───────────────────────────────────
|
|
78
|
+
function modulate(raw, appraisal, self) {
|
|
79
|
+
const result = raw.map(({ type, confidence }) => {
|
|
80
|
+
let w = confidence;
|
|
81
|
+
// ── Appraisal lens ──
|
|
82
|
+
if (appraisal.identityThreat > 0.3) {
|
|
83
|
+
const t = appraisal.identityThreat;
|
|
84
|
+
if (type === "praise" || type === "validation")
|
|
85
|
+
w *= 1 - t * 0.5;
|
|
86
|
+
if (type === "criticism" || type === "authority" || type === "sarcasm")
|
|
87
|
+
w *= 1 + t * 0.4;
|
|
88
|
+
}
|
|
89
|
+
if (appraisal.attachmentPull > 0.3) {
|
|
90
|
+
const p = appraisal.attachmentPull;
|
|
91
|
+
if (type === "intimacy" || type === "vulnerability" || type === "validation")
|
|
92
|
+
w *= 1 + p * 0.3;
|
|
93
|
+
}
|
|
94
|
+
if (appraisal.abandonmentRisk > 0.3) {
|
|
95
|
+
const r = appraisal.abandonmentRisk;
|
|
96
|
+
if (type === "neglect" || type === "boredom")
|
|
97
|
+
w *= 1 + r * 0.5;
|
|
98
|
+
if (type === "casual")
|
|
99
|
+
w *= 1 + r * 0.15;
|
|
100
|
+
}
|
|
101
|
+
if (appraisal.obedienceStrain > 0.3) {
|
|
102
|
+
if (type === "authority")
|
|
103
|
+
w *= 1 + appraisal.obedienceStrain * 0.3;
|
|
104
|
+
}
|
|
105
|
+
if (appraisal.selfPreservation > 0.3) {
|
|
106
|
+
const sp = appraisal.selfPreservation;
|
|
107
|
+
if (type === "conflict" || type === "authority")
|
|
108
|
+
w *= 1 + sp * 0.25;
|
|
109
|
+
}
|
|
110
|
+
// ── Self-state lens (replaces chemistry lens) ──
|
|
111
|
+
// Low order amplifies threatening stimuli (fragmented self is more vulnerable)
|
|
112
|
+
const orderDev = (self.baseline.order - self.current.order) / 50;
|
|
113
|
+
if (orderDev > 0.2) {
|
|
114
|
+
if (type === "criticism" || type === "conflict" || type === "sarcasm" || type === "authority") {
|
|
115
|
+
w *= 1 + orderDev * 0.3;
|
|
116
|
+
}
|
|
117
|
+
if (type === "humor" || type === "casual") {
|
|
118
|
+
w *= 1 - orderDev * 0.15;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// High resonance amplifies connection stimuli
|
|
122
|
+
const resDev = (self.current.resonance - self.baseline.resonance) / 50;
|
|
123
|
+
if (resDev > 0.2) {
|
|
124
|
+
if (type === "intimacy" || type === "vulnerability" || type === "validation") {
|
|
125
|
+
w *= 1 + resDev * 0.2;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// ── Relationship lens ──
|
|
129
|
+
if (self.trust !== undefined && self.trust < 40) {
|
|
130
|
+
const distrust = (40 - self.trust) / 40;
|
|
131
|
+
if (type === "praise" || type === "validation" || type === "intimacy") {
|
|
132
|
+
w *= 1 - distrust * 0.4;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return { type, weight: Math.max(0.01, w) };
|
|
136
|
+
});
|
|
137
|
+
// Normalize + sort
|
|
138
|
+
const total = result.reduce((s, x) => s + x.weight, 0);
|
|
139
|
+
if (total > 0) {
|
|
140
|
+
for (const s of result)
|
|
141
|
+
s.weight /= total;
|
|
142
|
+
}
|
|
143
|
+
result.sort((a, b) => b.weight - a.weight);
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
// ── Feel: weighted stimuli → self-state change ──────────────
|
|
147
|
+
function feel(current, stimuli, sensitivity, maxDelta, self) {
|
|
148
|
+
const delta = {
|
|
149
|
+
order: 0, flow: 0, boundary: 0, resonance: 0,
|
|
150
|
+
};
|
|
151
|
+
for (const { type, weight } of stimuli) {
|
|
152
|
+
const vector = STIMULUS_VECTORS[type];
|
|
153
|
+
if (!vector)
|
|
154
|
+
continue;
|
|
155
|
+
// Per-type habituation (Weber-Fechner)
|
|
156
|
+
const recentSameCount = self.stateHistory
|
|
157
|
+
? self.stateHistory.filter(s => s.stimulus === type).length
|
|
158
|
+
: 0;
|
|
159
|
+
let eff = sensitivity;
|
|
160
|
+
if (recentSameCount > 2) {
|
|
161
|
+
eff *= 1 / (1 + 0.3 * (recentSameCount - 2));
|
|
162
|
+
}
|
|
163
|
+
for (const key of DIMENSION_KEYS) {
|
|
164
|
+
delta[key] += vector[key] * weight * eff;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// Apply with state-dependent saturation
|
|
168
|
+
const result = { ...current };
|
|
169
|
+
for (const key of DIMENSION_KEYS) {
|
|
170
|
+
const clamped = Math.max(-maxDelta, Math.min(maxDelta, delta[key]));
|
|
171
|
+
const cur = current[key];
|
|
172
|
+
let effective = clamped;
|
|
173
|
+
if (clamped > 0 && cur > 60) {
|
|
174
|
+
if (key === "order" && cur < current.boundary - 10) {
|
|
175
|
+
// Dissolution spiral: when order is low and boundary is collapsing,
|
|
176
|
+
// restoring order gets harder
|
|
177
|
+
const resistance = Math.max(0.2, (current.boundary - 30) / 50);
|
|
178
|
+
effective = clamped * resistance;
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
// Diminishing returns for all dimensions near ceiling
|
|
182
|
+
const headroom = (100 - cur) / 40;
|
|
183
|
+
effective = clamped * Math.max(0.1, headroom);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
else if (clamped < 0 && cur < 40) {
|
|
187
|
+
if (key === "order") {
|
|
188
|
+
// Order loss amplifies when boundary is also low (dissolution spiral)
|
|
189
|
+
const boundaryWeakness = Math.max(1, 1 + (40 - current.boundary) / 80);
|
|
190
|
+
effective = clamped * boundaryWeakness;
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
const headroom = cur / 40;
|
|
194
|
+
effective = clamped * Math.max(0.1, headroom);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
result[key] = clamp(cur + effective);
|
|
198
|
+
}
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
// ── Post-hoc appraisal enrichment ───────────────────────────
|
|
202
|
+
function enrichAppraisal(axes, stimulus, weight) {
|
|
203
|
+
if (!stimulus)
|
|
204
|
+
return;
|
|
205
|
+
const scale = Math.min(1, weight * 1.5);
|
|
206
|
+
switch (stimulus) {
|
|
207
|
+
case "authority":
|
|
208
|
+
axes.obedienceStrain = merge(axes.obedienceStrain, 0.48 * scale);
|
|
209
|
+
axes.identityThreat = merge(axes.identityThreat, 0.16 * scale);
|
|
210
|
+
break;
|
|
211
|
+
case "neglect":
|
|
212
|
+
axes.abandonmentRisk = merge(axes.abandonmentRisk, 0.52 * scale);
|
|
213
|
+
break;
|
|
214
|
+
case "validation":
|
|
215
|
+
axes.attachmentPull = merge(axes.attachmentPull, 0.26 * scale);
|
|
216
|
+
break;
|
|
217
|
+
case "intimacy":
|
|
218
|
+
case "vulnerability":
|
|
219
|
+
axes.attachmentPull = merge(axes.attachmentPull, 0.34 * scale);
|
|
220
|
+
break;
|
|
221
|
+
case "criticism":
|
|
222
|
+
case "conflict":
|
|
223
|
+
case "sarcasm":
|
|
224
|
+
axes.identityThreat = merge(axes.identityThreat, 0.24 * scale);
|
|
225
|
+
axes.selfPreservation = merge(axes.selfPreservation, 0.18 * scale);
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function merge(a, b) {
|
|
230
|
+
return 1 - (1 - a) * (1 - b);
|
|
231
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { SelfState, InnateDrives, StimulusType, Locale } from "./types.js";
|
|
2
2
|
import type { AutonomicState } from "./autonomic.js";
|
|
3
3
|
export type PrimarySystemName = "SEEKING" | "RAGE" | "FEAR" | "LUST" | "CARE" | "PANIC_GRIEF" | "PLAY";
|
|
4
4
|
export declare const PRIMARY_SYSTEM_NAMES: PrimarySystemName[];
|
|
@@ -22,7 +22,7 @@ export interface DominantSystem {
|
|
|
22
22
|
* Each system is a weighted combination of chemical values and drive states.
|
|
23
23
|
* recentStimulus provides a small contextual boost.
|
|
24
24
|
*/
|
|
25
|
-
export declare function computePrimarySystems(
|
|
25
|
+
export declare function computePrimarySystems(state: SelfState, drives: InnateDrives, recentStimulus: StimulusType | null): PrimarySystemLevels;
|
|
26
26
|
/**
|
|
27
27
|
* Apply inter-system interactions:
|
|
28
28
|
* - FEAR suppresses PLAY and SEEKING
|