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.
@@ -1,10 +1,12 @@
1
- import type { ChemicalState, InnateDrives, Locale } from "./types.js";
1
+ import type { ChemicalState, InnateDrives, Locale, EnergyBudgets } from "./types.js";
2
2
  export type AutonomicState = "ventral-vagal" | "sympathetic" | "dorsal-vagal";
3
3
  export interface AutonomicResult {
4
4
  state: AutonomicState;
5
5
  transitionProgress: number;
6
6
  gatedEmotionCategories: string[];
7
7
  description: string;
8
+ processingDepth: number;
9
+ skippedStages: string[];
8
10
  }
9
11
  export interface AutonomicTransition {
10
12
  from: AutonomicState;
@@ -32,10 +34,26 @@ export declare function getTransitionTime(from: AutonomicState, to: AutonomicSta
32
34
  * Describe an autonomic state in the given locale.
33
35
  */
34
36
  export declare function describeAutonomicState(state: AutonomicState, locale: Locale): string;
37
+ /**
38
+ * P10: Compute processing depth from autonomic state and chemistry.
39
+ *
40
+ * Processing depth represents the cognitive resource available for reflection:
41
+ * - 0 = pure System 1 (intuition only, no metacognition)
42
+ * - 1 = full System 2 (complete reflective capacity)
43
+ *
44
+ * This is a natural extension of autonomic state: you can't deeply reflect
45
+ * when your nervous system is in fight/flight/freeze mode.
46
+ */
47
+ export declare function computeProcessingDepth(autonomicState: AutonomicState, chemistry: ChemicalState, baseline: ChemicalState, energyBudgets?: EnergyBudgets): {
48
+ depth: number;
49
+ skippedStages: string[];
50
+ };
35
51
  /**
36
52
  * Compute the full autonomic result with transition inertia.
37
53
  *
38
54
  * If previousState differs from the target state, transition progress
39
55
  * is based on elapsed time vs required transition time.
56
+ *
57
+ * Includes P10 processing depth (dual-process cognitive gating).
40
58
  */
41
- export declare function computeAutonomicResult(chemistry: ChemicalState, drives: InnateDrives, previousState: AutonomicState | null, minutesSinceLastUpdate: number, locale?: Locale): AutonomicResult;
59
+ export declare function computeAutonomicResult(chemistry: ChemicalState, drives: InnateDrives, previousState: AutonomicState | null, minutesSinceLastUpdate: number, locale?: Locale, baseline?: ChemicalState, energyBudgets?: EnergyBudgets): AutonomicResult;
package/dist/autonomic.js CHANGED
@@ -121,22 +121,85 @@ export function getTransitionTime(from, to) {
121
121
  export function describeAutonomicState(state, locale) {
122
122
  return AUTONOMIC_STRINGS[locale]?.[state] ?? AUTONOMIC_STRINGS.zh[state];
123
123
  }
124
+ /**
125
+ * P10: Compute processing depth from autonomic state and chemistry.
126
+ *
127
+ * Processing depth represents the cognitive resource available for reflection:
128
+ * - 0 = pure System 1 (intuition only, no metacognition)
129
+ * - 1 = full System 2 (complete reflective capacity)
130
+ *
131
+ * This is a natural extension of autonomic state: you can't deeply reflect
132
+ * when your nervous system is in fight/flight/freeze mode.
133
+ */
134
+ export function computeProcessingDepth(autonomicState, chemistry, baseline, energyBudgets) {
135
+ const { DA, HT, CORT, OT, NE, END } = chemistry;
136
+ // Chemical deviation from baseline (0-1)
137
+ let totalDeviation = 0;
138
+ const keys = ["DA", "HT", "CORT", "OT", "NE", "END"];
139
+ for (const k of keys) {
140
+ totalDeviation += Math.abs(chemistry[k] - baseline[k]);
141
+ }
142
+ const chemDeviation = Math.min(1, totalDeviation / 600);
143
+ // Base depth from autonomic state
144
+ let baseDepth;
145
+ if (autonomicState === "dorsal-vagal") {
146
+ baseDepth = 0;
147
+ }
148
+ else if (autonomicState === "sympathetic") {
149
+ // Higher CORT in sympathetic = less cognitive resource
150
+ baseDepth = CORT >= 60 ? 0.15 : 0.35;
151
+ }
152
+ else {
153
+ // ventral-vagal: safe, most cognitive resource available
154
+ baseDepth = 0.85;
155
+ }
156
+ // Chemical deviation reduces depth (strong emotions = less reflection)
157
+ let depth = Math.max(0, Math.min(1, baseDepth * (1 - chemDeviation * 0.5)));
158
+ // v9: Low attention energy further reduces processing depth
159
+ if (energyBudgets && energyBudgets.attention < 30) {
160
+ const attentionPenalty = (30 - energyBudgets.attention) / 30 * 0.3;
161
+ depth = Math.max(0, depth - attentionPenalty);
162
+ }
163
+ // Map depth to skipped pipeline stages
164
+ const skippedStages = [];
165
+ if (depth < 0.8)
166
+ skippedStages.push("generative-self");
167
+ if (depth < 0.5) {
168
+ skippedStages.push("ethics");
169
+ skippedStages.push("shared-intentionality");
170
+ }
171
+ if (depth < 0.2) {
172
+ skippedStages.push("metacognition");
173
+ skippedStages.push("experiential-field");
174
+ }
175
+ return { depth, skippedStages };
176
+ }
124
177
  /**
125
178
  * Compute the full autonomic result with transition inertia.
126
179
  *
127
180
  * If previousState differs from the target state, transition progress
128
181
  * is based on elapsed time vs required transition time.
182
+ *
183
+ * Includes P10 processing depth (dual-process cognitive gating).
129
184
  */
130
- export function computeAutonomicResult(chemistry, drives, previousState, minutesSinceLastUpdate, locale = "zh") {
185
+ export function computeAutonomicResult(chemistry, drives, previousState, minutesSinceLastUpdate, locale = "zh", baseline, energyBudgets) {
131
186
  const targetState = computeAutonomicState(chemistry, drives);
132
- // First call or same state immediate
133
- if (previousState === null || previousState === targetState) {
187
+ const effectiveBaseline = baseline ?? { DA: 50, HT: 50, CORT: 50, OT: 50, NE: 50, END: 50 };
188
+ // Helper to build full result with processing depth
189
+ const buildResult = (state, transitionProgress) => {
190
+ const { depth, skippedStages } = computeProcessingDepth(state, chemistry, effectiveBaseline, energyBudgets);
134
191
  return {
135
- state: targetState,
136
- transitionProgress: 1,
137
- gatedEmotionCategories: getGatedCategories(targetState),
138
- description: describeAutonomicState(targetState, locale),
192
+ state,
193
+ transitionProgress,
194
+ gatedEmotionCategories: getGatedCategories(state),
195
+ description: describeAutonomicState(state, locale),
196
+ processingDepth: depth,
197
+ skippedStages,
139
198
  };
199
+ };
200
+ // First call or same state — immediate
201
+ if (previousState === null || previousState === targetState) {
202
+ return buildResult(targetState, 1);
140
203
  }
141
204
  // Transitioning between states
142
205
  const transitionTime = getTransitionTime(previousState, targetState);
@@ -145,20 +208,10 @@ export function computeAutonomicResult(chemistry, drives, previousState, minutes
145
208
  : Math.min(1, minutesSinceLastUpdate / transitionTime);
146
209
  // If transition is complete, use the new state
147
210
  if (progress >= 1) {
148
- return {
149
- state: targetState,
150
- transitionProgress: 1,
151
- gatedEmotionCategories: getGatedCategories(targetState),
152
- description: describeAutonomicState(targetState, locale),
153
- };
211
+ return buildResult(targetState, 1);
154
212
  }
155
- // Transition in progress — still in previous state but progressing
156
- return {
157
- state: targetState,
158
- transitionProgress: progress,
159
- gatedEmotionCategories: getGatedCategories(targetState),
160
- description: describeAutonomicState(targetState, locale),
161
- };
213
+ // Transition in progress
214
+ return buildResult(targetState, progress);
162
215
  }
163
216
  // ── Internal Helpers ─────────────────────────────────────────
164
217
  /** Get the list of emotion categories that are blocked/gated for a state */
@@ -8,7 +8,7 @@ export declare function clamp(v: number): number;
8
8
  *
9
9
  * decayed = baseline + (current - baseline) * factor^(minutes/60)
10
10
  */
11
- export declare function applyDecay(current: ChemicalState, baseline: ChemicalState, minutesElapsed: number): ChemicalState;
11
+ export declare function applyDecay(current: ChemicalState, baseline: ChemicalState, minutesElapsed: number, decayRateModifiers?: Partial<Record<keyof ChemicalState, number>>): ChemicalState;
12
12
  /**
13
13
  * Apply a stimulus to the current state.
14
14
  * Respects emotional inertia (maxDelta) and personality sensitivity.
@@ -16,7 +16,7 @@ export declare function applyDecay(current: ChemicalState, baseline: ChemicalSta
16
16
  */
17
17
  export declare function applyStimulus(current: ChemicalState, stimulus: StimulusType, sensitivity: number, maxDelta: number, logger?: {
18
18
  warn: (msg: string) => void;
19
- }): ChemicalState;
19
+ }, recentSameCount?: number): ChemicalState;
20
20
  /**
21
21
  * Apply emotional contagion: the detected user emotion partially
22
22
  * influences the agent's chemistry.
package/dist/chemistry.js CHANGED
@@ -133,13 +133,22 @@ export function clamp(v) {
133
133
  *
134
134
  * decayed = baseline + (current - baseline) * factor^(minutes/60)
135
135
  */
136
- export function applyDecay(current, baseline, minutesElapsed) {
136
+ export function applyDecay(current, baseline, minutesElapsed, decayRateModifiers) {
137
137
  if (minutesElapsed <= 0)
138
138
  return { ...current };
139
139
  const result = { ...current };
140
140
  for (const key of CHEMICAL_KEYS) {
141
141
  const speed = CHEMICAL_DECAY_SPEED[key];
142
- const factor = Math.pow(DECAY_FACTORS[speed], minutesElapsed / 60);
142
+ const baseFactor = Math.pow(DECAY_FACTORS[speed], minutesElapsed / 60);
143
+ // v9: decayRateModifiers alter decay speed per chemical
144
+ // > 1 = slower recovery (trauma: factor closer to 1)
145
+ // < 1 = faster recovery (resilience: factor closer to 0)
146
+ let factor = baseFactor;
147
+ if (decayRateModifiers?.[key] !== undefined) {
148
+ const mod = decayRateModifiers[key];
149
+ // Raise the factor to the modifier power: mod>1 → slower decay, mod<1 → faster
150
+ factor = Math.pow(baseFactor, 1 / mod);
151
+ }
143
152
  result[key] = clamp(baseline[key] + (current[key] - baseline[key]) * factor);
144
153
  }
145
154
  return result;
@@ -149,15 +158,21 @@ export function applyDecay(current, baseline, minutesElapsed) {
149
158
  * Respects emotional inertia (maxDelta) and personality sensitivity.
150
159
  * Logs a warning for unknown stimulus types.
151
160
  */
152
- export function applyStimulus(current, stimulus, sensitivity, maxDelta, logger) {
161
+ export function applyStimulus(current, stimulus, sensitivity, maxDelta, logger, recentSameCount) {
153
162
  const vector = STIMULUS_VECTORS[stimulus];
154
163
  if (!vector) {
155
164
  logger?.warn(t("log.unknown_stimulus", "zh", { type: stimulus }));
156
165
  return { ...current };
157
166
  }
167
+ // v9: Habituation — Weber-Fechner diminishing returns
168
+ // First 2 exposures: 100%. 3rd: 77%, 5th: 53%, 10th: 29%
169
+ let effectiveSensitivity = sensitivity;
170
+ if (recentSameCount !== undefined && recentSameCount > 2) {
171
+ effectiveSensitivity *= 1 / (1 + 0.3 * (recentSameCount - 2));
172
+ }
158
173
  const result = { ...current };
159
174
  for (const key of CHEMICAL_KEYS) {
160
- const raw = vector[key] * sensitivity;
175
+ const raw = vector[key] * effectiveSensitivity;
161
176
  const clamped = Math.max(-maxDelta, Math.min(maxDelta, raw));
162
177
  result[key] = clamp(current[key] + clamped);
163
178
  }
@@ -1,4 +1,4 @@
1
- import type { ChemicalState } from "./types.js";
1
+ import type { ChemicalState, EnergyBudgets, StimulusType } from "./types.js";
2
2
  export type CircadianPhase = "morning" | "midday" | "afternoon" | "evening" | "night";
3
3
  /**
4
4
  * Classify a time into a circadian phase.
@@ -35,3 +35,21 @@ export declare function computeHomeostaticPressure(sessionMinutes: number): {
35
35
  daDepletion: number;
36
36
  neDepletion: number;
37
37
  };
38
+ /**
39
+ * Deplete energy budgets from a single interaction turn.
40
+ *
41
+ * - Attention: -3/turn base, extra for intellectual/conflict
42
+ * - Social energy: extraverts +2/turn (charging), introverts -3/turn (draining)
43
+ * - Decision capacity: varies by stimulus complexity
44
+ *
45
+ * Extraverts can exceed 100 (up to 120 — "supercharged").
46
+ */
47
+ export declare function computeEnergyDepletion(budgets: EnergyBudgets, stimulus: StimulusType | null, isExtravert: boolean): EnergyBudgets;
48
+ /**
49
+ * Recover energy budgets during absence (between sessions or long pauses).
50
+ *
51
+ * - Attention: +20/hour
52
+ * - Social energy: extraverts -3/hour (drain when alone), introverts +15/hour (recharge)
53
+ * - Decision capacity: +25/hour
54
+ */
55
+ export declare function computeEnergyRecovery(budgets: EnergyBudgets, minutesElapsed: number, isExtravert: boolean): EnergyBudgets;
package/dist/circadian.js CHANGED
@@ -95,3 +95,69 @@ export function computeHomeostaticPressure(sessionMinutes) {
95
95
  neDepletion: parseFloat((base * 2.5).toFixed(4)),
96
96
  };
97
97
  }
98
+ // ── Energy Budgets (v9) ─────────────────────────────────────
99
+ // Finite cognitive/social resources that deplete during interaction.
100
+ // Extraverts GAIN social energy from interaction; introverts LOSE it.
101
+ /** Stimulus-specific attention costs (higher = more draining) */
102
+ const ATTENTION_COSTS = {
103
+ intellectual: 5,
104
+ conflict: 5,
105
+ authority: 4,
106
+ vulnerability: 3,
107
+ sarcasm: 3,
108
+ criticism: 3,
109
+ surprise: 2,
110
+ };
111
+ /** Stimulus-specific decision costs */
112
+ const DECISION_COSTS = {
113
+ conflict: 4,
114
+ authority: 4,
115
+ vulnerability: 3,
116
+ criticism: 2,
117
+ sarcasm: 2,
118
+ };
119
+ /**
120
+ * Deplete energy budgets from a single interaction turn.
121
+ *
122
+ * - Attention: -3/turn base, extra for intellectual/conflict
123
+ * - Social energy: extraverts +2/turn (charging), introverts -3/turn (draining)
124
+ * - Decision capacity: varies by stimulus complexity
125
+ *
126
+ * Extraverts can exceed 100 (up to 120 — "supercharged").
127
+ */
128
+ export function computeEnergyDepletion(budgets, stimulus, isExtravert) {
129
+ const extravertMax = 120;
130
+ const introvertMax = 100;
131
+ const max = isExtravert ? extravertMax : introvertMax;
132
+ // Attention: base -3, extra from stimulus
133
+ const attentionCost = 3 + (stimulus ? (ATTENTION_COSTS[stimulus] ?? 0) : 0);
134
+ const attention = clamp(budgets.attention - attentionCost, 0, 100);
135
+ // Social energy: E charges, I drains
136
+ const socialDelta = isExtravert ? 2 : -3;
137
+ const socialEnergy = clamp(budgets.socialEnergy + socialDelta, 0, max);
138
+ // Decision capacity: base -1, extra from stimulus
139
+ const decisionCost = 1 + (stimulus ? (DECISION_COSTS[stimulus] ?? 0) : 0);
140
+ const decisionCapacity = clamp(budgets.decisionCapacity - decisionCost, 0, 100);
141
+ return { attention, socialEnergy, decisionCapacity };
142
+ }
143
+ /**
144
+ * Recover energy budgets during absence (between sessions or long pauses).
145
+ *
146
+ * - Attention: +20/hour
147
+ * - Social energy: extraverts -3/hour (drain when alone), introverts +15/hour (recharge)
148
+ * - Decision capacity: +25/hour
149
+ */
150
+ export function computeEnergyRecovery(budgets, minutesElapsed, isExtravert) {
151
+ if (minutesElapsed <= 0)
152
+ return { ...budgets };
153
+ const hours = minutesElapsed / 60;
154
+ const extravertMax = 120;
155
+ const introvertMax = 100;
156
+ const max = isExtravert ? extravertMax : introvertMax;
157
+ const attention = clamp(budgets.attention + hours * 20, 0, 100);
158
+ // E drains alone, I recharges alone
159
+ const socialDelta = isExtravert ? -3 * hours : 15 * hours;
160
+ const socialEnergy = clamp(budgets.socialEnergy + socialDelta, 0, max);
161
+ const decisionCapacity = clamp(budgets.decisionCapacity + hours * 25, 0, 100);
162
+ return { attention, socialEnergy, decisionCapacity };
163
+ }
package/dist/core.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { PsycheState, StimulusType, Locale, MBTIType, OutcomeScore, PsycheMode, PersonalityTraits } from "./types.js";
1
+ import type { PsycheState, StimulusType, Locale, MBTIType, OutcomeScore, PsycheMode, PersonalityTraits, PolicyModifiers } from "./types.js";
2
2
  import type { StorageAdapter } from "./storage.js";
3
3
  export interface PsycheEngineConfig {
4
4
  mbti?: MBTIType;
@@ -25,6 +25,8 @@ export interface ProcessInputResult {
25
25
  dynamicContext: string;
26
26
  /** Detected stimulus type from user input, null if none */
27
27
  stimulus: StimulusType | null;
28
+ /** v9: Structured behavioral policy modifiers — machine-readable "off baseline" signals */
29
+ policyModifiers?: PolicyModifiers;
28
30
  }
29
31
  export interface ProcessOutputResult {
30
32
  /** LLM output with <psyche_update> tags stripped */
package/dist/core.js CHANGED
@@ -11,7 +11,7 @@
11
11
  //
12
12
  // Orchestrates: chemistry, classify, prompt, profiles, guards, learning
13
13
  // ============================================================
14
- import { DEFAULT_RELATIONSHIP, DEFAULT_DRIVES, DEFAULT_LEARNING_STATE, DEFAULT_METACOGNITIVE_STATE, DEFAULT_PERSONHOOD_STATE } from "./types.js";
14
+ import { DEFAULT_RELATIONSHIP, DEFAULT_DRIVES, DEFAULT_LEARNING_STATE, DEFAULT_METACOGNITIVE_STATE, DEFAULT_PERSONHOOD_STATE, DEFAULT_ENERGY_BUDGETS, DEFAULT_TRAIT_DRIFT } from "./types.js";
15
15
  import { MemoryStorageAdapter } from "./storage.js";
16
16
  import { applyDecay, applyStimulus, applyContagion, clamp, describeEmotionalState } from "./chemistry.js";
17
17
  import { classifyStimulus } from "./classify.js";
@@ -23,12 +23,12 @@ import { decayDrives, feedDrives, detectExistentialThreat, computeEffectiveBasel
23
23
  import { checkForUpdate } from "./update.js";
24
24
  import { evaluateOutcome, computeContextHash, updateLearnedVector, predictChemistry, recordPrediction, } from "./learning.js";
25
25
  import { assessMetacognition } from "./metacognition.js";
26
- import { buildDecisionContext } from "./decision-bias.js";
26
+ import { buildDecisionContext, computePolicyModifiers, buildPolicyContext } from "./decision-bias.js";
27
27
  import { computeExperientialField } from "./experiential-field.js";
28
28
  import { computeGenerativeSelf } from "./generative-self.js";
29
29
  import { updateSharedIntentionality, buildSharedIntentionalityContext } from "./shared-intentionality.js";
30
30
  import { assessEthics, buildEthicalContext } from "./ethics.js";
31
- import { computeCircadianModulation, computeHomeostaticPressure } from "./circadian.js";
31
+ import { computeCircadianModulation, computeHomeostaticPressure, computeEnergyDepletion, computeEnergyRecovery } from "./circadian.js";
32
32
  import { computeAutonomicResult } from "./autonomic.js";
33
33
  import { computePrimarySystems, computeSystemInteractions, gatePrimarySystemsByAutonomic, describeBehavioralTendencies, } from "./primary-systems.js";
34
34
  const NOOP_LOGGER = { info: () => { }, warn: () => { }, debug: () => { } };
@@ -91,6 +91,16 @@ export class PsycheEngine {
91
91
  loaded.sessionStartedAt = new Date().toISOString();
92
92
  loaded.version = 7;
93
93
  }
94
+ // Migrate v7 → v8: P8 Barrett construction + P10 processing depth + P11 memory consolidation
95
+ // No data changes needed — ChemicalSnapshot new fields are optional (backward compatible)
96
+ if (loaded.version < 8) {
97
+ loaded.version = 8;
98
+ }
99
+ // Migrate v8 → v9: PolicyModifiers, TraitDrift, EnergyBudgets, Habituation
100
+ // All new fields are optional — no data migration needed
101
+ if (loaded.version < 9) {
102
+ loaded.version = 9;
103
+ }
94
104
  this.state = loaded;
95
105
  }
96
106
  else {
@@ -136,13 +146,13 @@ export class PsycheEngine {
136
146
  // Decay drives first — needs build up over time
137
147
  const decayedDrives = decayDrives(state.drives, minutesElapsed);
138
148
  // Compute effective baseline from drives (unsatisfied drives shift baseline)
139
- const effectiveBaseline = computeEffectiveBaseline(state.baseline, decayedDrives);
149
+ const effectiveBaseline = computeEffectiveBaseline(state.baseline, decayedDrives, state.traitDrift);
140
150
  // P12: Apply circadian rhythm modulation to effective baseline
141
151
  const circadianBaseline = computeCircadianModulation(now, effectiveBaseline);
142
152
  state = {
143
153
  ...state,
144
154
  drives: decayedDrives,
145
- current: applyDecay(state.current, circadianBaseline, minutesElapsed),
155
+ current: applyDecay(state.current, circadianBaseline, minutesElapsed, state.traitDrift?.decayRateModifiers),
146
156
  updatedAt: now.toISOString(),
147
157
  };
148
158
  }
@@ -164,6 +174,12 @@ export class PsycheEngine {
164
174
  },
165
175
  };
166
176
  }
177
+ // v9: Energy budget recovery during absence + depletion per turn
178
+ const isExtravert = this.cfg.mbti.startsWith("E");
179
+ const currentBudgets = state.energyBudgets ?? { ...DEFAULT_ENERGY_BUDGETS };
180
+ let energyBudgets = minutesElapsed >= 5
181
+ ? computeEnergyRecovery(currentBudgets, minutesElapsed, isExtravert)
182
+ : { ...currentBudgets };
167
183
  // Classify user stimulus and apply chemistry
168
184
  let appliedStimulus = null;
169
185
  if (text.length > 0) {
@@ -181,13 +197,19 @@ export class PsycheEngine {
181
197
  appliedStimulus = primary.type;
182
198
  // Feed drives from stimulus, then apply stimulus with drive-modified sensitivity
183
199
  drives = feedDrives(drives, primary.type);
184
- const effectiveSensitivity = computeEffectiveSensitivity(getSensitivity(state.mbti), drives, primary.type);
200
+ const effectiveSensitivity = computeEffectiveSensitivity(getSensitivity(state.mbti), drives, primary.type, state.traitDrift);
185
201
  const modeMultiplier = this.cfg.mode === "work" ? 0.3 : this.cfg.mode === "companion" ? 1.5 : 1.0;
186
202
  const effectiveMaxDelta = this.cfg.mode === "work" ? 5 : this.cfg.maxChemicalDelta;
187
- current = applyStimulus(current, primary.type, effectiveSensitivity * this.cfg.personalityIntensity * modeMultiplier, effectiveMaxDelta, NOOP_LOGGER);
203
+ // v9: Habituation count recent same-type stimuli in this session
204
+ const recentSameCount = (state.emotionalHistory ?? [])
205
+ .filter(s => s.stimulus === primary.type).length + 1; // +1 for current
206
+ current = applyStimulus(current, primary.type, effectiveSensitivity * this.cfg.personalityIntensity * modeMultiplier, effectiveMaxDelta, NOOP_LOGGER, recentSameCount);
188
207
  }
189
208
  state = { ...state, drives, current };
190
209
  }
210
+ // v9: Deplete energy budgets from this interaction turn
211
+ energyBudgets = computeEnergyDepletion(energyBudgets, appliedStimulus, isExtravert);
212
+ state = { ...state, energyBudgets };
191
213
  // Conversation warmth: sustained interaction → gentle DA/OT rise, CORT drop
192
214
  // Simulates the natural "warm glow" of being in continuous conversation
193
215
  const turnsSoFar = (state.emotionalHistory ?? []).length;
@@ -205,35 +227,40 @@ export class PsycheEngine {
205
227
  }
206
228
  // ── Locale (used by multiple subsystems below) ──────────
207
229
  const locale = state.meta.locale ?? this.cfg.locale;
208
- // ── P7: Autonomic nervous system Polyvagal state ────────
209
- const autonomicResult = computeAutonomicResult(state.current, state.drives, state.autonomicState ?? null, minutesElapsed, locale);
230
+ // ── P7+P10: Autonomic nervous system + Processing depth ────
231
+ const autonomicResult = computeAutonomicResult(state.current, state.drives, state.autonomicState ?? null, minutesElapsed, locale, state.baseline, state.energyBudgets);
210
232
  state = {
211
233
  ...state,
212
234
  autonomicState: autonomicResult.state,
213
235
  };
236
+ const skip = new Set(autonomicResult.skippedStages);
214
237
  // ── P9: Primary emotional systems (Panksepp) ──────────────
215
238
  const rawSystems = computePrimarySystems(state.current, state.drives, appliedStimulus);
216
239
  const interactedSystems = computeSystemInteractions(rawSystems);
217
240
  const gatedSystems = gatePrimarySystemsByAutonomic(interactedSystems, autonomicResult.state);
218
241
  const primarySystemsDescription = describeBehavioralTendencies(gatedSystems, locale);
219
242
  // ── Metacognition: assess emotional state before acting ────
220
- const metacognitiveAssessment = assessMetacognition(state, appliedStimulus ?? "casual", state.learning.outcomeHistory);
221
- // Apply self-soothing regulation if suggested with high confidence
222
- for (const reg of metacognitiveAssessment.regulationSuggestions) {
223
- if (reg.strategy === "self-soothing" && reg.confidence >= 0.6 && reg.chemistryAdjustment) {
224
- const adj = reg.chemistryAdjustment;
225
- state = {
226
- ...state,
227
- current: {
228
- ...state.current,
229
- DA: clamp(state.current.DA + (adj.DA ?? 0)),
230
- HT: clamp(state.current.HT + (adj.HT ?? 0)),
231
- CORT: clamp(state.current.CORT + (adj.CORT ?? 0)),
232
- OT: clamp(state.current.OT + (adj.OT ?? 0)),
233
- NE: clamp(state.current.NE + (adj.NE ?? 0)),
234
- END: clamp(state.current.END + (adj.END ?? 0)),
235
- },
236
- };
243
+ // P10: Skip metacognition when processingDepth < 0.2 (System 1 mode)
244
+ let metacognitiveAssessment = null;
245
+ if (!skip.has("metacognition")) {
246
+ metacognitiveAssessment = assessMetacognition(state, appliedStimulus ?? "casual", state.learning.outcomeHistory);
247
+ // Apply self-soothing regulation if suggested with high confidence
248
+ for (const reg of metacognitiveAssessment.regulationSuggestions) {
249
+ if (reg.strategy === "self-soothing" && reg.confidence >= 0.6 && reg.chemistryAdjustment) {
250
+ const adj = reg.chemistryAdjustment;
251
+ state = {
252
+ ...state,
253
+ current: {
254
+ ...state.current,
255
+ DA: clamp(state.current.DA + (adj.DA ?? 0)),
256
+ HT: clamp(state.current.HT + (adj.HT ?? 0)),
257
+ CORT: clamp(state.current.CORT + (adj.CORT ?? 0)),
258
+ OT: clamp(state.current.OT + (adj.OT ?? 0)),
259
+ NE: clamp(state.current.NE + (adj.NE ?? 0)),
260
+ END: clamp(state.current.END + (adj.END ?? 0)),
261
+ },
262
+ };
263
+ }
237
264
  }
238
265
  }
239
266
  // Push snapshot to emotional history
@@ -248,7 +275,7 @@ export class PsycheEngine {
248
275
  // ── Generate prediction for next turn's auto-learning ────
249
276
  if (appliedStimulus) {
250
277
  const ctxHash = computeContextHash(state, opts?.userId);
251
- const effectiveSensitivity = computeEffectiveSensitivity(getSensitivity(state.mbti), state.drives, appliedStimulus);
278
+ const effectiveSensitivity = computeEffectiveSensitivity(getSensitivity(state.mbti), state.drives, appliedStimulus, state.traitDrift);
252
279
  const predicted = predictChemistry(preInteractionState.current, appliedStimulus, state.learning, ctxHash, effectiveSensitivity, this.cfg.maxChemicalDelta);
253
280
  this.pendingPrediction = {
254
281
  predictedChemistry: predicted,
@@ -260,15 +287,30 @@ export class PsycheEngine {
260
287
  else {
261
288
  this.pendingPrediction = null;
262
289
  }
263
- // ── P6: Digital Personhood computations ────────────────────
264
- // Experiential field — unified inner experience
265
- const experientialField = computeExperientialField(state, metacognitiveAssessment);
290
+ // ── P6: Digital Personhood computations (P10-gated) ────────
291
+ // Experiential field — unified inner experience (P8: Barrett construction context)
292
+ const constructionContext = {
293
+ autonomicState: autonomicResult.state,
294
+ stimulus: appliedStimulus,
295
+ relationshipPhase: (state.relationships._default ?? state.relationships[Object.keys(state.relationships)[0]])?.phase,
296
+ predictionError: state.learning.predictionHistory.length > 0
297
+ ? state.learning.predictionHistory[state.learning.predictionHistory.length - 1].predictionError
298
+ : undefined,
299
+ };
300
+ const experientialField = skip.has("experiential-field")
301
+ ? null
302
+ : computeExperientialField(state, metacognitiveAssessment ?? undefined, undefined, constructionContext);
266
303
  // Shared intentionality — theory of mind + joint attention
267
- const sharedState = updateSharedIntentionality(state, appliedStimulus, opts?.userId);
304
+ const sharedState = skip.has("shared-intentionality")
305
+ ? null
306
+ : updateSharedIntentionality(state, appliedStimulus, opts?.userId);
268
307
  // Ethics — emotional self-care check
269
- const ethicalAssessment = assessEthics(state);
308
+ const ethicalAssessment = skip.has("ethics")
309
+ ? null
310
+ : assessEthics(state);
270
311
  // Generative self — update identity narrative periodically (every 10 turns)
271
- if (state.meta.totalInteractions % 10 === 0 && state.meta.totalInteractions > 0) {
312
+ if (!skip.has("generative-self")
313
+ && state.meta.totalInteractions % 10 === 0 && state.meta.totalInteractions > 0) {
272
314
  const selfModel = computeGenerativeSelf(state);
273
315
  state = {
274
316
  ...state,
@@ -286,7 +328,7 @@ export class PsycheEngine {
286
328
  };
287
329
  }
288
330
  // Persist ethical concerns if significant
289
- if (ethicalAssessment.ethicalHealth < 0.7) {
331
+ if (ethicalAssessment && ethicalAssessment.ethicalHealth < 0.7) {
290
332
  const newConcerns = ethicalAssessment.concerns
291
333
  .filter((c) => c.severity > 0.4)
292
334
  .map((c) => ({ type: c.type, severity: c.severity, timestamp: new Date().toISOString() }));
@@ -304,7 +346,7 @@ export class PsycheEngine {
304
346
  }
305
347
  }
306
348
  // Persist theory of mind
307
- if (sharedState.theoryOfMind.confidence > 0.3) {
349
+ if (sharedState && sharedState.theoryOfMind.confidence > 0.3) {
308
350
  const userId = opts?.userId ?? "_default";
309
351
  state = {
310
352
  ...state,
@@ -326,11 +368,25 @@ export class PsycheEngine {
326
368
  this.state = state;
327
369
  await this.storage.save(state);
328
370
  // Build metacognitive and decision context strings
329
- const metacogNote = metacognitiveAssessment.metacognitiveNote;
371
+ const metacogNote = metacognitiveAssessment?.metacognitiveNote;
330
372
  const decisionCtx = buildDecisionContext(state);
331
- const ethicsCtx = buildEthicalContext(ethicalAssessment, locale);
332
- const sharedCtx = buildSharedIntentionalityContext(sharedState, locale);
333
- const experientialNarrative = experientialField.narrative || undefined;
373
+ const ethicsCtx = ethicalAssessment ? buildEthicalContext(ethicalAssessment, locale) : undefined;
374
+ const sharedCtx = sharedState ? buildSharedIntentionalityContext(sharedState, locale) : undefined;
375
+ const experientialNarrative = experientialField?.narrative || undefined;
376
+ // v9: Compute structured policy modifiers
377
+ const policyModifiers = computePolicyModifiers(state);
378
+ const policyCtx = buildPolicyContext(policyModifiers, locale);
379
+ // P10: Append processing depth info to autonomic description when depth is low
380
+ let autonomicDesc;
381
+ if (autonomicResult.state !== "ventral-vagal") {
382
+ autonomicDesc = autonomicResult.description;
383
+ if (autonomicResult.processingDepth < 0.5) {
384
+ const depthNote = locale === "en"
385
+ ? " Reflective capacity reduced — intuitive reactions."
386
+ : "反思能力降低——直觉反应中。";
387
+ autonomicDesc += depthNote;
388
+ }
389
+ }
334
390
  if (this.cfg.compactMode) {
335
391
  return {
336
392
  systemContext: "",
@@ -343,12 +399,12 @@ export class PsycheEngine {
343
399
  ethicsContext: ethicsCtx || undefined,
344
400
  sharedIntentionalityContext: sharedCtx || undefined,
345
401
  experientialNarrative: experientialNarrative,
346
- // Only inject autonomic description when NOT in default calm state (saves ~25 tokens)
347
- autonomicDescription: autonomicResult.state !== "ventral-vagal"
348
- ? autonomicResult.description : undefined,
402
+ autonomicDescription: autonomicDesc,
349
403
  primarySystemsDescription: primarySystemsDescription || undefined,
404
+ policyContext: policyCtx || undefined,
350
405
  }),
351
406
  stimulus: appliedStimulus,
407
+ policyModifiers,
352
408
  };
353
409
  }
354
410
  return {
@@ -359,11 +415,12 @@ export class PsycheEngine {
359
415
  ethicsContext: ethicsCtx || undefined,
360
416
  sharedIntentionalityContext: sharedCtx || undefined,
361
417
  experientialNarrative: experientialNarrative,
362
- autonomicDescription: autonomicResult.state !== "ventral-vagal"
363
- ? autonomicResult.description : undefined,
418
+ autonomicDescription: autonomicDesc,
364
419
  primarySystemsDescription: primarySystemsDescription || undefined,
420
+ policyContext: policyCtx || undefined,
365
421
  }),
366
422
  stimulus: appliedStimulus,
423
+ policyModifiers,
367
424
  };
368
425
  }
369
426
  /**
@@ -399,7 +456,7 @@ export class PsycheEngine {
399
456
  ...state,
400
457
  drives: feedDrives(state.drives, parseResult.llmStimulus),
401
458
  };
402
- const effectiveSensitivity = computeEffectiveSensitivity(getSensitivity(state.mbti), state.drives, parseResult.llmStimulus);
459
+ const effectiveSensitivity = computeEffectiveSensitivity(getSensitivity(state.mbti), state.drives, parseResult.llmStimulus, state.traitDrift);
403
460
  state = {
404
461
  ...state,
405
462
  current: applyStimulus(state.current, parseResult.llmStimulus, effectiveSensitivity, this.cfg.maxChemicalDelta, NOOP_LOGGER),
@@ -502,7 +559,7 @@ export class PsycheEngine {
502
559
  const selfModel = getDefaultSelfModel(mbti);
503
560
  const now = new Date().toISOString();
504
561
  return {
505
- version: 7,
562
+ version: 9,
506
563
  mbti,
507
564
  baseline,
508
565
  current: { ...baseline },
@@ -519,6 +576,8 @@ export class PsycheEngine {
519
576
  personhood: { ...DEFAULT_PERSONHOOD_STATE },
520
577
  autonomicState: "ventral-vagal",
521
578
  sessionStartedAt: now,
579
+ traitDrift: { ...DEFAULT_TRAIT_DRIFT },
580
+ energyBudgets: { ...DEFAULT_ENERGY_BUDGETS },
522
581
  meta: {
523
582
  agentName: name,
524
583
  createdAt: now,