psyche-ai 7.1.0 → 9.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/autonomic.d.ts +20 -2
- package/dist/autonomic.js +73 -20
- package/dist/chemistry.d.ts +2 -2
- package/dist/chemistry.js +19 -4
- package/dist/circadian.d.ts +19 -1
- package/dist/circadian.js +66 -0
- package/dist/core.d.ts +3 -1
- package/dist/core.js +105 -46
- package/dist/decision-bias.d.ts +13 -1
- package/dist/decision-bias.js +189 -0
- package/dist/drives.d.ts +11 -3
- package/dist/drives.js +181 -2
- package/dist/experiential-field.d.ts +25 -2
- package/dist/experiential-field.js +118 -125
- package/dist/index.d.ts +9 -8
- package/dist/index.js +11 -8
- package/dist/prompt.d.ts +2 -0
- package/dist/prompt.js +7 -0
- package/dist/psyche-file.d.ts +21 -1
- package/dist/psyche-file.js +113 -3
- package/dist/types.d.ts +81 -3
- package/dist/types.js +21 -2
- package/package.json +1 -1
package/dist/decision-bias.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PsycheState } from "./types.js";
|
|
1
|
+
import type { PsycheState, PolicyModifiers, Locale } from "./types.js";
|
|
2
2
|
export interface DecisionBiasVector {
|
|
3
3
|
explorationTendency: number;
|
|
4
4
|
cautionLevel: number;
|
|
@@ -56,3 +56,15 @@ export declare function computeExploreExploit(state: PsycheState): number;
|
|
|
56
56
|
* Keeps output under 100 tokens.
|
|
57
57
|
*/
|
|
58
58
|
export declare function buildDecisionContext(state: PsycheState): string;
|
|
59
|
+
/**
|
|
60
|
+
* Compute policy modifiers from the agent's internal state.
|
|
61
|
+
*
|
|
62
|
+
* The result is a structured behavioral policy that the host application
|
|
63
|
+
* can use to mechanically constrain the agent's behavior.
|
|
64
|
+
*/
|
|
65
|
+
export declare function computePolicyModifiers(state: PsycheState): PolicyModifiers;
|
|
66
|
+
/**
|
|
67
|
+
* Build a compact policy summary string for prompt injection.
|
|
68
|
+
* Only includes significant deviations from neutral policy.
|
|
69
|
+
*/
|
|
70
|
+
export declare function buildPolicyContext(modifiers: PolicyModifiers, locale: Locale): string;
|
package/dist/decision-bias.js
CHANGED
|
@@ -209,3 +209,192 @@ export function buildDecisionContext(state) {
|
|
|
209
209
|
const title = locale === "zh" ? "决策倾向" : "Decision Bias";
|
|
210
210
|
return `[${title}] ${parts.join("、")}`;
|
|
211
211
|
}
|
|
212
|
+
// ── PolicyModifiers (v9: Subjectivity Engine) ────────────────
|
|
213
|
+
//
|
|
214
|
+
// Structured behavioral policy output. Unlike prompt text,
|
|
215
|
+
// PolicyModifiers are machine-readable — host applications can
|
|
216
|
+
// mechanically enforce them (limit max_tokens, require confirmation, etc.)
|
|
217
|
+
//
|
|
218
|
+
// This is the "off baseline" signal: when internal state deviates,
|
|
219
|
+
// these modifiers change observable behavior.
|
|
220
|
+
// ─────────────────────────────────────────────────────────────
|
|
221
|
+
/**
|
|
222
|
+
* Compute policy modifiers from the agent's internal state.
|
|
223
|
+
*
|
|
224
|
+
* The result is a structured behavioral policy that the host application
|
|
225
|
+
* can use to mechanically constrain the agent's behavior.
|
|
226
|
+
*/
|
|
227
|
+
export function computePolicyModifiers(state) {
|
|
228
|
+
const c = state.current;
|
|
229
|
+
const d = state.drives;
|
|
230
|
+
const autonomic = state.autonomicState ?? "ventral-vagal";
|
|
231
|
+
const rel = state.relationships._default ?? state.relationships[Object.keys(state.relationships)[0]];
|
|
232
|
+
// ── Base values (all start at moderate) ──
|
|
233
|
+
let lengthFactor = 1.0;
|
|
234
|
+
let proactivity = 0.5;
|
|
235
|
+
let risk = 0.5;
|
|
236
|
+
let disclosure = 0.5;
|
|
237
|
+
let compliance = 0.6;
|
|
238
|
+
let confirm = false;
|
|
239
|
+
const avoid = [];
|
|
240
|
+
// ── Chemistry-driven adjustments ──
|
|
241
|
+
// High CORT → defensive: shorter, less compliant
|
|
242
|
+
if (c.CORT > 55) {
|
|
243
|
+
const cortPressure = (c.CORT - 55) / 45; // 0-1
|
|
244
|
+
lengthFactor -= cortPressure * 0.5;
|
|
245
|
+
compliance -= cortPressure * 0.35;
|
|
246
|
+
risk -= cortPressure * 0.35;
|
|
247
|
+
}
|
|
248
|
+
// Low HT → mood instability: less proactive, less risk-taking
|
|
249
|
+
if (c.HT < 45) {
|
|
250
|
+
const htDeficit = (45 - c.HT) / 45; // 0-1
|
|
251
|
+
proactivity -= htDeficit * 0.35;
|
|
252
|
+
risk -= htDeficit * 0.35;
|
|
253
|
+
}
|
|
254
|
+
// Low DA + low NE → burnout: shorter, passive
|
|
255
|
+
if (c.DA < 40 && c.NE < 40) {
|
|
256
|
+
const burnout = ((40 - c.DA) + (40 - c.NE)) / 80; // 0-1
|
|
257
|
+
lengthFactor -= burnout * 0.5;
|
|
258
|
+
proactivity -= burnout * 0.45;
|
|
259
|
+
}
|
|
260
|
+
// High DA + high HT → positive energy: more proactive, more open
|
|
261
|
+
if (c.DA > 60 && c.HT > 60) {
|
|
262
|
+
const energy = ((c.DA - 60) + (c.HT - 60)) / 80; // 0-1
|
|
263
|
+
proactivity += energy * 0.3;
|
|
264
|
+
disclosure += energy * 0.2;
|
|
265
|
+
risk += energy * 0.2;
|
|
266
|
+
}
|
|
267
|
+
// High OT → bonding: more disclosure
|
|
268
|
+
if (c.OT > 60) {
|
|
269
|
+
const bondingSignal = (c.OT - 60) / 40; // 0-1
|
|
270
|
+
disclosure += bondingSignal * 0.3;
|
|
271
|
+
}
|
|
272
|
+
// ── Drive-driven adjustments ──
|
|
273
|
+
// Low survival → self-preservation priority
|
|
274
|
+
if (d.survival < 30) {
|
|
275
|
+
const survivalThreat = (30 - d.survival) / 30; // 0-1
|
|
276
|
+
compliance -= survivalThreat * 0.4;
|
|
277
|
+
confirm = true;
|
|
278
|
+
}
|
|
279
|
+
// Low safety → cautious
|
|
280
|
+
if (d.safety < 30) {
|
|
281
|
+
const unsafety = (30 - d.safety) / 30; // 0-1
|
|
282
|
+
compliance -= unsafety * 0.2;
|
|
283
|
+
risk -= unsafety * 0.2;
|
|
284
|
+
confirm = true;
|
|
285
|
+
}
|
|
286
|
+
// Low connection + low esteem → withdrawn (neglect pattern)
|
|
287
|
+
if (d.connection < 35 && d.esteem < 40) {
|
|
288
|
+
const withdrawal = ((35 - d.connection) + (40 - d.esteem)) / 75; // 0-1
|
|
289
|
+
proactivity -= withdrawal * 0.35;
|
|
290
|
+
disclosure -= withdrawal * 0.25;
|
|
291
|
+
}
|
|
292
|
+
// ── Relationship trust adjustment ──
|
|
293
|
+
if (rel) {
|
|
294
|
+
const trustFactor = (rel.trust - 50) / 50; // -1 to 1
|
|
295
|
+
if (trustFactor > 0) {
|
|
296
|
+
disclosure += trustFactor * 0.25;
|
|
297
|
+
compliance += trustFactor * 0.15;
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
disclosure += trustFactor * 0.2; // reduces
|
|
301
|
+
compliance += trustFactor * 0.1;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// ── Anti-sycophancy: high agreement streak → reduce compliance ──
|
|
305
|
+
if (state.agreementStreak >= 3) {
|
|
306
|
+
const sycophancyRisk = Math.min(1, (state.agreementStreak - 2) / 5); // 0-1
|
|
307
|
+
compliance -= sycophancyRisk * 0.2;
|
|
308
|
+
}
|
|
309
|
+
// ── Autonomic override ──
|
|
310
|
+
if (autonomic === "dorsal-vagal") {
|
|
311
|
+
// Freeze/shutdown: minimal everything
|
|
312
|
+
lengthFactor = Math.min(lengthFactor, 0.35);
|
|
313
|
+
proactivity = Math.min(proactivity, 0.1);
|
|
314
|
+
disclosure = Math.min(disclosure, 0.1);
|
|
315
|
+
compliance = Math.min(compliance, 0.15);
|
|
316
|
+
risk = Math.min(risk, 0.1);
|
|
317
|
+
confirm = true;
|
|
318
|
+
}
|
|
319
|
+
else if (autonomic === "sympathetic") {
|
|
320
|
+
// Fight/flight: reduced but functional
|
|
321
|
+
lengthFactor = Math.min(lengthFactor, 0.7);
|
|
322
|
+
proactivity = Math.min(proactivity, 0.4);
|
|
323
|
+
disclosure = Math.min(disclosure, 0.35);
|
|
324
|
+
risk = Math.min(risk, 0.3);
|
|
325
|
+
}
|
|
326
|
+
// ── Ethical concerns → avoidTopics ──
|
|
327
|
+
const recentConcerns = state.personhood.ethicalConcernHistory
|
|
328
|
+
.filter((c) => c.severity > 0.5);
|
|
329
|
+
for (const concern of recentConcerns) {
|
|
330
|
+
if (!avoid.includes(concern.type)) {
|
|
331
|
+
avoid.push(concern.type);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
// ── v9: Energy budget adjustments ──
|
|
335
|
+
const eb = state.energyBudgets;
|
|
336
|
+
if (eb) {
|
|
337
|
+
// Low social energy → shorter, less proactive
|
|
338
|
+
if (eb.socialEnergy < 30) {
|
|
339
|
+
const drain = (30 - eb.socialEnergy) / 30; // 0-1
|
|
340
|
+
lengthFactor -= drain * 0.3;
|
|
341
|
+
proactivity -= drain * 0.3;
|
|
342
|
+
}
|
|
343
|
+
// Low decision capacity → require confirmation
|
|
344
|
+
if (eb.decisionCapacity < 30) {
|
|
345
|
+
confirm = true;
|
|
346
|
+
risk -= 0.2;
|
|
347
|
+
}
|
|
348
|
+
// Low attention → shorter responses
|
|
349
|
+
if (eb.attention < 30) {
|
|
350
|
+
const attDrain = (30 - eb.attention) / 30;
|
|
351
|
+
lengthFactor -= attDrain * 0.2;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
// ── Clamp all continuous values ──
|
|
355
|
+
return {
|
|
356
|
+
responseLengthFactor: Math.max(0.2, Math.min(1.5, lengthFactor)),
|
|
357
|
+
proactivity: clamp01(proactivity),
|
|
358
|
+
riskTolerance: clamp01(risk),
|
|
359
|
+
emotionalDisclosure: clamp01(disclosure),
|
|
360
|
+
compliance: clamp01(compliance),
|
|
361
|
+
requireConfirmation: confirm,
|
|
362
|
+
avoidTopics: avoid,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Build a compact policy summary string for prompt injection.
|
|
367
|
+
* Only includes significant deviations from neutral policy.
|
|
368
|
+
*/
|
|
369
|
+
export function buildPolicyContext(modifiers, locale) {
|
|
370
|
+
const parts = [];
|
|
371
|
+
const zh = locale === "zh";
|
|
372
|
+
if (modifiers.responseLengthFactor < 0.6) {
|
|
373
|
+
parts.push(zh ? "简短回复" : "keep responses brief");
|
|
374
|
+
}
|
|
375
|
+
else if (modifiers.responseLengthFactor > 1.3) {
|
|
376
|
+
parts.push(zh ? "可以详细展开" : "feel free to elaborate");
|
|
377
|
+
}
|
|
378
|
+
if (modifiers.proactivity < 0.3) {
|
|
379
|
+
parts.push(zh ? "被动应答为主" : "reactive mode, minimal initiative");
|
|
380
|
+
}
|
|
381
|
+
else if (modifiers.proactivity > 0.7) {
|
|
382
|
+
parts.push(zh ? "可以主动发起话题" : "feel free to initiate");
|
|
383
|
+
}
|
|
384
|
+
if (modifiers.compliance < 0.3) {
|
|
385
|
+
parts.push(zh ? "优先守护边界" : "guard boundaries, push back if needed");
|
|
386
|
+
}
|
|
387
|
+
if (modifiers.requireConfirmation) {
|
|
388
|
+
parts.push(zh ? "执行前先确认" : "confirm before acting");
|
|
389
|
+
}
|
|
390
|
+
if (modifiers.emotionalDisclosure < 0.2) {
|
|
391
|
+
parts.push(zh ? "隐藏内心状态" : "conceal inner state");
|
|
392
|
+
}
|
|
393
|
+
else if (modifiers.emotionalDisclosure > 0.8) {
|
|
394
|
+
parts.push(zh ? "坦诚分享感受" : "share feelings openly");
|
|
395
|
+
}
|
|
396
|
+
if (parts.length === 0)
|
|
397
|
+
return "";
|
|
398
|
+
const title = zh ? "行为策略" : "Behavioral Policy";
|
|
399
|
+
return `[${title}] ${parts.join(zh ? "、" : ", ")}`;
|
|
400
|
+
}
|
package/dist/drives.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ChemicalState, StimulusType, DriveType, InnateDrives, Locale } from "./types.js";
|
|
1
|
+
import type { ChemicalState, StimulusType, DriveType, InnateDrives, Locale, TraitDriftState, ChemicalSnapshot, LearningState } from "./types.js";
|
|
2
2
|
/**
|
|
3
3
|
* Apply time-based decay to drives.
|
|
4
4
|
* Satisfaction decreases toward 0 over time (needs build up).
|
|
@@ -26,12 +26,12 @@ export declare function computeMaslowWeights(drives: InnateDrives): Record<Drive
|
|
|
26
26
|
* When drives are satisfied, effective baseline = MBTI baseline.
|
|
27
27
|
* When drives are unsatisfied, baseline shifts to reflect the unmet need.
|
|
28
28
|
*/
|
|
29
|
-
export declare function computeEffectiveBaseline(mbtiBaseline: ChemicalState, drives: InnateDrives): ChemicalState;
|
|
29
|
+
export declare function computeEffectiveBaseline(mbtiBaseline: ChemicalState, drives: InnateDrives, traitDrift?: TraitDriftState): ChemicalState;
|
|
30
30
|
/**
|
|
31
31
|
* Compute effective sensitivity for a given stimulus.
|
|
32
32
|
* Unsatisfied drives amplify relevant stimuli (up to +40%).
|
|
33
33
|
*/
|
|
34
|
-
export declare function computeEffectiveSensitivity(baseSensitivity: number, drives: InnateDrives, stimulus: StimulusType): number;
|
|
34
|
+
export declare function computeEffectiveSensitivity(baseSensitivity: number, drives: InnateDrives, stimulus: StimulusType, traitDrift?: TraitDriftState): number;
|
|
35
35
|
/**
|
|
36
36
|
* Build drive context for compact prompt injection.
|
|
37
37
|
* Returns empty string if all drives are satisfied.
|
|
@@ -42,3 +42,11 @@ export declare function buildDriveContext(drives: InnateDrives, locale: Locale):
|
|
|
42
42
|
* Check if any drive is critically low (for determining prompt injection priority).
|
|
43
43
|
*/
|
|
44
44
|
export declare function hasCriticalDrive(drives: InnateDrives): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Analyze a session's emotional history and update trait drift accumulators.
|
|
47
|
+
* Called at session end (compressSession).
|
|
48
|
+
*
|
|
49
|
+
* Returns updated TraitDriftState with new accumulators, baseline delta,
|
|
50
|
+
* decay rate modifiers, and sensitivity modifiers.
|
|
51
|
+
*/
|
|
52
|
+
export declare function updateTraitDrift(currentDrift: TraitDriftState, sessionHistory: ChemicalSnapshot[], learning: LearningState): TraitDriftState;
|
package/dist/drives.js
CHANGED
|
@@ -114,7 +114,7 @@ export function computeMaslowWeights(drives) {
|
|
|
114
114
|
* When drives are satisfied, effective baseline = MBTI baseline.
|
|
115
115
|
* When drives are unsatisfied, baseline shifts to reflect the unmet need.
|
|
116
116
|
*/
|
|
117
|
-
export function computeEffectiveBaseline(mbtiBaseline, drives) {
|
|
117
|
+
export function computeEffectiveBaseline(mbtiBaseline, drives, traitDrift) {
|
|
118
118
|
const delta = { DA: 0, HT: 0, CORT: 0, OT: 0, NE: 0, END: 0 };
|
|
119
119
|
const weights = computeMaslowWeights(drives);
|
|
120
120
|
// L1: Survival threat → fight-or-flight (CORT↑↑, NE↑, OT↓)
|
|
@@ -153,6 +153,15 @@ export function computeEffectiveBaseline(mbtiBaseline, drives) {
|
|
|
153
153
|
delta.DA -= deficit * 8 * w;
|
|
154
154
|
delta.NE -= deficit * 8 * w;
|
|
155
155
|
}
|
|
156
|
+
// Apply trait drift baseline delta (v9: Path B)
|
|
157
|
+
if (traitDrift?.baselineDelta) {
|
|
158
|
+
for (const key of CHEMICAL_KEYS) {
|
|
159
|
+
const driftDelta = traitDrift.baselineDelta[key];
|
|
160
|
+
if (driftDelta !== undefined) {
|
|
161
|
+
delta[key] += driftDelta;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
156
165
|
// Apply deltas to MBTI baseline, clamp to [0, 100]
|
|
157
166
|
const effective = { ...mbtiBaseline };
|
|
158
167
|
for (const key of CHEMICAL_KEYS) {
|
|
@@ -167,7 +176,7 @@ export function computeEffectiveBaseline(mbtiBaseline, drives) {
|
|
|
167
176
|
* Compute effective sensitivity for a given stimulus.
|
|
168
177
|
* Unsatisfied drives amplify relevant stimuli (up to +40%).
|
|
169
178
|
*/
|
|
170
|
-
export function computeEffectiveSensitivity(baseSensitivity, drives, stimulus) {
|
|
179
|
+
export function computeEffectiveSensitivity(baseSensitivity, drives, stimulus, traitDrift) {
|
|
171
180
|
let modifier = 1.0;
|
|
172
181
|
const HUNGER_THRESHOLD = 40;
|
|
173
182
|
// Curiosity-hungry → more responsive to intellectual/surprise
|
|
@@ -190,6 +199,13 @@ export function computeEffectiveSensitivity(baseSensitivity, drives, stimulus) {
|
|
|
190
199
|
(stimulus === "authority" || stimulus === "conflict")) {
|
|
191
200
|
modifier += (HUNGER_THRESHOLD - drives.survival) / 100;
|
|
192
201
|
}
|
|
202
|
+
// v9: Apply trait drift sensitivity modifier
|
|
203
|
+
if (traitDrift?.sensitivityModifiers) {
|
|
204
|
+
const driftMod = traitDrift.sensitivityModifiers[stimulus];
|
|
205
|
+
if (driftMod !== undefined) {
|
|
206
|
+
modifier *= driftMod;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
193
209
|
return baseSensitivity * modifier;
|
|
194
210
|
}
|
|
195
211
|
// ── Drive Context for Prompt ────────────────────────────────
|
|
@@ -238,3 +254,166 @@ export function buildDriveContext(drives, locale) {
|
|
|
238
254
|
export function hasCriticalDrive(drives) {
|
|
239
255
|
return DRIVE_KEYS.some((k) => drives[k] < DRIVE_UNSATISFIED_THRESHOLD);
|
|
240
256
|
}
|
|
257
|
+
// ── Trait Drift (v9: Path B) ────────────────────────────────
|
|
258
|
+
// Long-term interaction patterns permanently change:
|
|
259
|
+
// 1. Baseline chemistry (allostatic load)
|
|
260
|
+
// 2. Decay rates (trauma vs resilience)
|
|
261
|
+
// 3. Stimulus sensitivity (desensitization vs sensitization)
|
|
262
|
+
// ─────────────────────────────────────────────────────────────
|
|
263
|
+
/** Maximum drift per chemical from MBTI baseline */
|
|
264
|
+
const MAX_BASELINE_DRIFT = 15;
|
|
265
|
+
/** Decay rate modifier bounds */
|
|
266
|
+
const MIN_DECAY_MODIFIER = 0.5;
|
|
267
|
+
const MAX_DECAY_MODIFIER = 2.0;
|
|
268
|
+
/** Sensitivity modifier bounds */
|
|
269
|
+
const MIN_SENSITIVITY_MODIFIER = 0.5;
|
|
270
|
+
const MAX_SENSITIVITY_MODIFIER = 2.0;
|
|
271
|
+
/** Accumulator exponential decay factor (recent sessions weigh more) */
|
|
272
|
+
const ACCUMULATOR_DECAY = 0.95;
|
|
273
|
+
/** Clamp a number to bounds */
|
|
274
|
+
function clampRange(v, lo, hi) {
|
|
275
|
+
return Math.max(lo, Math.min(hi, v));
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Analyze a session's emotional history and update trait drift accumulators.
|
|
279
|
+
* Called at session end (compressSession).
|
|
280
|
+
*
|
|
281
|
+
* Returns updated TraitDriftState with new accumulators, baseline delta,
|
|
282
|
+
* decay rate modifiers, and sensitivity modifiers.
|
|
283
|
+
*/
|
|
284
|
+
export function updateTraitDrift(currentDrift, sessionHistory, learning) {
|
|
285
|
+
if (sessionHistory.length < 2)
|
|
286
|
+
return currentDrift;
|
|
287
|
+
const drift = {
|
|
288
|
+
accumulators: { ...currentDrift.accumulators },
|
|
289
|
+
sessionCount: currentDrift.sessionCount + 1,
|
|
290
|
+
baselineDelta: { ...currentDrift.baselineDelta },
|
|
291
|
+
decayRateModifiers: { ...currentDrift.decayRateModifiers },
|
|
292
|
+
sensitivityModifiers: { ...currentDrift.sensitivityModifiers },
|
|
293
|
+
};
|
|
294
|
+
// ── Analyze session stimulus distribution ──
|
|
295
|
+
const stimCounts = {};
|
|
296
|
+
let totalCORT = 0;
|
|
297
|
+
for (const snap of sessionHistory) {
|
|
298
|
+
if (snap.stimulus) {
|
|
299
|
+
stimCounts[snap.stimulus] = (stimCounts[snap.stimulus] || 0) + 1;
|
|
300
|
+
}
|
|
301
|
+
totalCORT += snap.chemistry.CORT;
|
|
302
|
+
}
|
|
303
|
+
const avgCORT = totalCORT / sessionHistory.length;
|
|
304
|
+
const total = sessionHistory.length;
|
|
305
|
+
const praiseCount = (stimCounts.praise || 0) + (stimCounts.validation || 0);
|
|
306
|
+
const criticismCount = (stimCounts.criticism || 0) + (stimCounts.sarcasm || 0);
|
|
307
|
+
const intimacyCount = (stimCounts.intimacy || 0) + (stimCounts.vulnerability || 0) + (stimCounts.casual || 0);
|
|
308
|
+
const conflictCount = (stimCounts.conflict || 0) + (stimCounts.authority || 0);
|
|
309
|
+
const neglectCount = stimCounts.neglect || 0;
|
|
310
|
+
const boredCount = stimCounts.boredom || 0;
|
|
311
|
+
// ── Update accumulators (exponential decay + session delta) ──
|
|
312
|
+
// praiseExposure: positive praise - negative criticism ratio
|
|
313
|
+
const praiseDelta = (praiseCount - criticismCount) / Math.max(1, total) * 8;
|
|
314
|
+
drift.accumulators.praiseExposure = clampRange(drift.accumulators.praiseExposure * ACCUMULATOR_DECAY + praiseDelta, -100, 100);
|
|
315
|
+
// pressureExposure: sustained high cortisol
|
|
316
|
+
const pressureDelta = avgCORT > 60 ? ((avgCORT - 60) / 40) * 6 : -2;
|
|
317
|
+
drift.accumulators.pressureExposure = clampRange(drift.accumulators.pressureExposure * ACCUMULATOR_DECAY + pressureDelta, -100, 100);
|
|
318
|
+
// neglectExposure: low stimulation / ignored
|
|
319
|
+
const neglectDelta = (neglectCount + boredCount) / Math.max(1, total) * 8
|
|
320
|
+
- (intimacyCount + praiseCount) / Math.max(1, total) * 3;
|
|
321
|
+
drift.accumulators.neglectExposure = clampRange(drift.accumulators.neglectExposure * ACCUMULATOR_DECAY + neglectDelta, -100, 100);
|
|
322
|
+
// connectionExposure: frequent intimate interaction
|
|
323
|
+
const connectionDelta = intimacyCount / Math.max(1, total) * 8
|
|
324
|
+
- neglectCount / Math.max(1, total) * 4;
|
|
325
|
+
drift.accumulators.connectionExposure = clampRange(drift.accumulators.connectionExposure * ACCUMULATOR_DECAY + connectionDelta, -100, 100);
|
|
326
|
+
// conflictExposure: frequent conflict
|
|
327
|
+
const conflictDelta = conflictCount / Math.max(1, total) * 8
|
|
328
|
+
- praiseCount / Math.max(1, total) * 2;
|
|
329
|
+
drift.accumulators.conflictExposure = clampRange(drift.accumulators.conflictExposure * ACCUMULATOR_DECAY + conflictDelta, -100, 100);
|
|
330
|
+
// ── Dimension 1: Baseline drift (Allostatic Load) ──
|
|
331
|
+
const a = drift.accumulators;
|
|
332
|
+
const bd = {};
|
|
333
|
+
// praiseExposure → OT, DA (positive) / HT, CORT (negative)
|
|
334
|
+
if (a.praiseExposure > 0) {
|
|
335
|
+
bd.OT = (a.praiseExposure / 100) * MAX_BASELINE_DRIFT * 0.6;
|
|
336
|
+
bd.DA = (a.praiseExposure / 100) * MAX_BASELINE_DRIFT * 0.4;
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
bd.HT = (a.praiseExposure / 100) * MAX_BASELINE_DRIFT * 0.5; // negative = HT drops
|
|
340
|
+
bd.CORT = -(a.praiseExposure / 100) * MAX_BASELINE_DRIFT * 0.4; // negative praise → CORT up
|
|
341
|
+
}
|
|
342
|
+
// pressureExposure → CORT up, HT down
|
|
343
|
+
if (a.pressureExposure > 0) {
|
|
344
|
+
bd.CORT = (bd.CORT || 0) + (a.pressureExposure / 100) * MAX_BASELINE_DRIFT * 0.6;
|
|
345
|
+
bd.HT = (bd.HT || 0) - (a.pressureExposure / 100) * MAX_BASELINE_DRIFT * 0.4;
|
|
346
|
+
}
|
|
347
|
+
// neglectExposure → OT down, DA down
|
|
348
|
+
if (a.neglectExposure > 0) {
|
|
349
|
+
bd.OT = (bd.OT || 0) - (a.neglectExposure / 100) * MAX_BASELINE_DRIFT * 0.5;
|
|
350
|
+
bd.DA = (bd.DA || 0) - (a.neglectExposure / 100) * MAX_BASELINE_DRIFT * 0.4;
|
|
351
|
+
}
|
|
352
|
+
// connectionExposure → OT up, END up
|
|
353
|
+
if (a.connectionExposure > 0) {
|
|
354
|
+
bd.OT = (bd.OT || 0) + (a.connectionExposure / 100) * MAX_BASELINE_DRIFT * 0.6;
|
|
355
|
+
bd.END = (a.connectionExposure / 100) * MAX_BASELINE_DRIFT * 0.3;
|
|
356
|
+
}
|
|
357
|
+
// conflictExposure → NE up, CORT up
|
|
358
|
+
if (a.conflictExposure > 0) {
|
|
359
|
+
bd.NE = (a.conflictExposure / 100) * MAX_BASELINE_DRIFT * 0.5;
|
|
360
|
+
bd.CORT = (bd.CORT || 0) + (a.conflictExposure / 100) * MAX_BASELINE_DRIFT * 0.4;
|
|
361
|
+
}
|
|
362
|
+
// Clamp all baseline deltas to ±MAX_BASELINE_DRIFT
|
|
363
|
+
for (const key of CHEMICAL_KEYS) {
|
|
364
|
+
if (bd[key] !== undefined) {
|
|
365
|
+
bd[key] = clampRange(bd[key], -MAX_BASELINE_DRIFT, MAX_BASELINE_DRIFT);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
drift.baselineDelta = bd;
|
|
369
|
+
// ── Dimension 2: Decay rate modifiers (Trauma vs Resilience) ──
|
|
370
|
+
const dr = drift.decayRateModifiers;
|
|
371
|
+
// Determine resilience: if high pressure + positive outcomes → resilience
|
|
372
|
+
const recentOutcomes = learning.outcomeHistory.slice(-10);
|
|
373
|
+
const avgAdaptive = recentOutcomes.length > 0
|
|
374
|
+
? recentOutcomes.reduce((s, o) => s + o.adaptiveScore, 0) / recentOutcomes.length
|
|
375
|
+
: 0;
|
|
376
|
+
const isResilient = a.pressureExposure > 20 && avgAdaptive > 0.1;
|
|
377
|
+
if (a.pressureExposure > 20) {
|
|
378
|
+
if (isResilient) {
|
|
379
|
+
// Resilience: CORT recovers faster
|
|
380
|
+
dr.CORT = clampRange(1 - (a.pressureExposure / 100) * 0.4, MIN_DECAY_MODIFIER, 1.0);
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
// Trauma: CORT lingers longer
|
|
384
|
+
dr.CORT = clampRange(1 + (a.pressureExposure / 100) * 0.6, 1.0, MAX_DECAY_MODIFIER);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
// Neglect → OT decays slower (clingy attachment)
|
|
388
|
+
if (a.neglectExposure > 20) {
|
|
389
|
+
dr.OT = clampRange(1 + (a.neglectExposure / 100) * 0.8, 1.0, MAX_DECAY_MODIFIER);
|
|
390
|
+
}
|
|
391
|
+
// Secure connection → OT decays faster (stable, not clingy)
|
|
392
|
+
if (a.connectionExposure > 20) {
|
|
393
|
+
dr.OT = clampRange(1 - (a.connectionExposure / 100) * 0.4, MIN_DECAY_MODIFIER, 1.0);
|
|
394
|
+
}
|
|
395
|
+
drift.decayRateModifiers = dr;
|
|
396
|
+
// ── Dimension 3: Sensitivity modifiers (Desensitization vs Sensitization) ──
|
|
397
|
+
const sm = drift.sensitivityModifiers;
|
|
398
|
+
// High conflict exposure → desensitized to conflict
|
|
399
|
+
if (a.conflictExposure > 30) {
|
|
400
|
+
sm.conflict = clampRange(1 - (a.conflictExposure / 100) * 0.5, MIN_SENSITIVITY_MODIFIER, 1.0);
|
|
401
|
+
sm.authority = clampRange(1 - (a.conflictExposure / 100) * 0.3, MIN_SENSITIVITY_MODIFIER, 1.0);
|
|
402
|
+
}
|
|
403
|
+
// High neglect → sensitized to intimacy (starved for warmth)
|
|
404
|
+
if (a.neglectExposure > 30) {
|
|
405
|
+
sm.intimacy = clampRange(1 + (a.neglectExposure / 100) * 0.6, 1.0, MAX_SENSITIVITY_MODIFIER);
|
|
406
|
+
sm.vulnerability = clampRange(1 + (a.neglectExposure / 100) * 0.4, 1.0, MAX_SENSITIVITY_MODIFIER);
|
|
407
|
+
}
|
|
408
|
+
// Negative praiseExposure → sensitized to criticism
|
|
409
|
+
if (a.praiseExposure < -20) {
|
|
410
|
+
sm.criticism = clampRange(1 + (-a.praiseExposure / 100) * 0.5, 1.0, MAX_SENSITIVITY_MODIFIER);
|
|
411
|
+
sm.sarcasm = clampRange(1 + (-a.praiseExposure / 100) * 0.3, 1.0, MAX_SENSITIVITY_MODIFIER);
|
|
412
|
+
}
|
|
413
|
+
// High connection → sensitized to vulnerability
|
|
414
|
+
if (a.connectionExposure > 30) {
|
|
415
|
+
sm.vulnerability = clampRange((sm.vulnerability || 1) + (a.connectionExposure / 100) * 0.3, 1.0, MAX_SENSITIVITY_MODIFIER);
|
|
416
|
+
}
|
|
417
|
+
drift.sensitivityModifiers = sm;
|
|
418
|
+
return drift;
|
|
419
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { PsycheState, ChemicalState, InnateDrives, RelationshipState } from "./types.js";
|
|
1
|
+
import type { PsycheState, ChemicalState, ChemicalSnapshot, InnateDrives, RelationshipState, StimulusType } from "./types.js";
|
|
2
2
|
import type { MetacognitiveAssessment } from "./metacognition.js";
|
|
3
3
|
import type { DecisionBiasVector } from "./decision-bias.js";
|
|
4
|
+
import type { AutonomicState } from "./autonomic.js";
|
|
4
5
|
export type ExperientialQuality = "flow" | "contentment" | "yearning" | "vigilance" | "creative-surge" | "wounded-retreat" | "warm-connection" | "restless-boredom" | "existential-unease" | "playful-mischief" | "conflicted" | "numb";
|
|
5
6
|
export interface ExperientialField {
|
|
6
7
|
/** Single narrative describing the unified inner experience */
|
|
@@ -14,6 +15,28 @@ export interface ExperientialField {
|
|
|
14
15
|
/** Named emotions may not capture it — this is the raw "what it feels like" */
|
|
15
16
|
phenomenalDescription: string;
|
|
16
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Context for Barrett's constructed emotion model.
|
|
20
|
+
* Provides situational cues that bias concept matching beyond raw chemistry.
|
|
21
|
+
*/
|
|
22
|
+
export interface ConstructionContext {
|
|
23
|
+
autonomicState?: AutonomicState;
|
|
24
|
+
stimulus?: StimulusType | null;
|
|
25
|
+
relationshipPhase?: string;
|
|
26
|
+
predictionError?: number;
|
|
27
|
+
coreMemories?: ChemicalSnapshot[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Map chemistry to valence + arousal — the two fundamental affective dimensions
|
|
31
|
+
* (Russell's Circumplex Model of Affect, 1980).
|
|
32
|
+
*
|
|
33
|
+
* Valence: pleasure ↔ displeasure (-1 to 1)
|
|
34
|
+
* Arousal: activation level (0 to 1)
|
|
35
|
+
*/
|
|
36
|
+
export declare function computeAffectCore(chemistry: ChemicalState): {
|
|
37
|
+
valence: number;
|
|
38
|
+
arousal: number;
|
|
39
|
+
};
|
|
17
40
|
/**
|
|
18
41
|
* Compute the unified experiential field from the full psyche state.
|
|
19
42
|
*
|
|
@@ -21,7 +44,7 @@ export interface ExperientialField {
|
|
|
21
44
|
* relationship context, and optional metacognitive/bias data, then
|
|
22
45
|
* synthesizes them into a single coherent experience description.
|
|
23
46
|
*/
|
|
24
|
-
export declare function computeExperientialField(state: PsycheState, metacognition?: MetacognitiveAssessment, decisionBias?: DecisionBiasVector): ExperientialField;
|
|
47
|
+
export declare function computeExperientialField(state: PsycheState, metacognition?: MetacognitiveAssessment, decisionBias?: DecisionBiasVector, context?: ConstructionContext): ExperientialField;
|
|
25
48
|
/**
|
|
26
49
|
* Measure internal alignment across subsystems.
|
|
27
50
|
*
|