psyche-ai 3.0.0 → 4.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/prompt.js CHANGED
@@ -15,7 +15,7 @@ import { getChannelProfile, buildChannelModifier } from "./channels.js";
15
15
  *
16
16
  * This is the "current moment" — what the agent is feeling RIGHT NOW.
17
17
  */
18
- export function buildDynamicContext(state, userId) {
18
+ export function buildDynamicContext(state, userId, opts) {
19
19
  const { current, baseline, mbti, empathyLog, selfModel, meta, agreementStreak, emotionalHistory } = state;
20
20
  const locale = meta.locale ?? "zh";
21
21
  const relationship = getRelationship(state, userId);
@@ -88,6 +88,16 @@ export function buildDynamicContext(state, userId) {
88
88
  if (reciprocity) {
89
89
  parts.push("", reciprocity);
90
90
  }
91
+ // Metacognitive awareness (P5)
92
+ if (opts?.metacognitiveNote) {
93
+ const mcTitle = locale === "zh" ? "元认知" : "Metacognition";
94
+ parts.push("", `[${mcTitle}] ${opts.metacognitiveNote}`);
95
+ }
96
+ // Decision bias context (P5)
97
+ if (opts?.decisionContext) {
98
+ const dbTitle = locale === "zh" ? "决策倾向" : "Decision Bias";
99
+ parts.push("", `[${dbTitle}] ${opts.decisionContext}`);
100
+ }
91
101
  parts.push("", agencyReminder, sycophancyWarning, "", t("dynamic.update_reminder", locale));
92
102
  return parts.filter((l) => l !== undefined).join("\n");
93
103
  }
@@ -478,7 +488,19 @@ export function buildCompactContext(state, userId, opts) {
478
488
  if (selfCtx)
479
489
  parts.push(selfCtx);
480
490
  }
481
- // 9. Cross-session emotional memory — surface relationship history
491
+ // 9. Metacognitive awareness (P5)
492
+ if (opts?.metacognitiveNote) {
493
+ parts.push(locale === "zh"
494
+ ? `[元认知] ${opts.metacognitiveNote}`
495
+ : `[Metacognition] ${opts.metacognitiveNote}`);
496
+ }
497
+ // 9b. Decision bias (P5) — only if significantly non-neutral
498
+ if (opts?.decisionContext) {
499
+ parts.push(locale === "zh"
500
+ ? `[决策倾向] ${opts.decisionContext}`
501
+ : `[Decision Bias] ${opts.decisionContext}`);
502
+ }
503
+ // 10. Cross-session emotional memory — surface relationship history
482
504
  const relationship = getRelationship(state, userId);
483
505
  if (relationship.memory && relationship.memory.length > 0) {
484
506
  const memLines = relationship.memory.slice(-3); // last 3 sessions
@@ -486,12 +508,12 @@ export function buildCompactContext(state, userId, opts) {
486
508
  ? `[记忆]\n${memLines.join("\n")}`
487
509
  : `[Memory]\n${memLines.join("\n")}`);
488
510
  }
489
- // 10. Channel modifier — expression style per platform (between memory and empathy)
511
+ // 11. Channel modifier — expression style per platform (between memory and empathy)
490
512
  if (opts?.channelType) {
491
513
  const channelProfile = getChannelProfile(opts.channelType);
492
514
  parts.push(buildChannelModifier(channelProfile, locale));
493
515
  }
494
- // 11. Empathy report — only when user shares feelings
516
+ // 12. Empathy report — only when user shares feelings
495
517
  parts.push(locale === "zh"
496
518
  ? `如果对方在分享感受,在回复末尾用 <psyche_update> 报告:\nuserState: 对方情绪\nprojectedFeeling: 你的感受\nresonance: match|partial|mismatch\n否则不需要报告。`
497
519
  : `If user shares feelings, report at end with <psyche_update>:\nuserState: their emotion\nprojectedFeeling: your feeling\nresonance: match|partial|mismatch\nOtherwise no report needed.`);
@@ -4,7 +4,7 @@
4
4
  // ============================================================
5
5
  import { readFile, writeFile, access, rename, constants } from "node:fs/promises";
6
6
  import { join } from "node:path";
7
- import { CHEMICAL_KEYS, CHEMICAL_NAMES, CHEMICAL_NAMES_ZH, DEFAULT_RELATIONSHIP, DEFAULT_DRIVES, DEFAULT_LEARNING_STATE, MAX_EMOTIONAL_HISTORY, MAX_RELATIONSHIP_MEMORY, } from "./types.js";
7
+ import { CHEMICAL_KEYS, CHEMICAL_NAMES, CHEMICAL_NAMES_ZH, DEFAULT_RELATIONSHIP, DEFAULT_DRIVES, DEFAULT_LEARNING_STATE, DEFAULT_METACOGNITIVE_STATE, MAX_EMOTIONAL_HISTORY, MAX_RELATIONSHIP_MEMORY, } from "./types.js";
8
8
  import { getBaseline, getDefaultSelfModel, extractMBTI, getSensitivity, getTemperament } from "./profiles.js";
9
9
  import { applyDecay, detectEmotions } from "./chemistry.js";
10
10
  import { decayDrives, computeEffectiveBaseline } from "./drives.js";
@@ -241,11 +241,13 @@ export function migrateToLatest(raw, fallbackName) {
241
241
  }
242
242
  // v2→v3: add drives
243
243
  // v3→v4: add learning
244
+ // v4→v5: add metacognition
244
245
  return {
245
246
  ...state,
246
- version: 4,
247
+ version: 5,
247
248
  drives: state.drives ?? { ...DEFAULT_DRIVES },
248
249
  learning: state.learning ?? { ...DEFAULT_LEARNING_STATE },
250
+ metacognition: state.metacognition ?? { ...DEFAULT_METACOGNITIVE_STATE },
249
251
  };
250
252
  }
251
253
  /**
@@ -259,7 +261,7 @@ export async function initializeState(workspaceDir, opts, logger = NOOP_LOGGER)
259
261
  const selfModel = getDefaultSelfModel(mbti);
260
262
  const now = new Date().toISOString();
261
263
  const state = {
262
- version: 4,
264
+ version: 5,
263
265
  mbti,
264
266
  baseline,
265
267
  current: { ...baseline },
@@ -274,6 +276,7 @@ export async function initializeState(workspaceDir, opts, logger = NOOP_LOGGER)
274
276
  agreementStreak: 0,
275
277
  lastDisagreement: null,
276
278
  learning: { ...DEFAULT_LEARNING_STATE },
279
+ metacognition: { ...DEFAULT_METACOGNITIVE_STATE },
277
280
  meta: {
278
281
  agentName,
279
282
  createdAt: now,
@@ -0,0 +1,38 @@
1
+ import type { ChemicalState, ChemicalSnapshot, StimulusType, PsycheState, RelationshipState } from "./types.js";
2
+ export interface StimulusPrediction {
3
+ stimulus: StimulusType;
4
+ probability: number;
5
+ }
6
+ export interface AnticipationState {
7
+ predictions: StimulusPrediction[];
8
+ anticipatoryChemistry: Partial<ChemicalState>;
9
+ timestamp: string;
10
+ }
11
+ export interface RegretEntry {
12
+ turnIndex: number;
13
+ counterfactualDelta: Partial<ChemicalState>;
14
+ regretIntensity: number;
15
+ description: string;
16
+ timestamp: string;
17
+ }
18
+ /**
19
+ * Predict likely next stimulus based on interaction history.
20
+ * Uses simple Markov property: given recent stimulus sequence, what comes next?
21
+ */
22
+ export declare function predictNextStimulus(emotionalHistory: ChemicalSnapshot[], relationshipPhase: RelationshipState["phase"]): StimulusPrediction[];
23
+ /**
24
+ * Generate anticipatory chemistry changes based on predictions.
25
+ * High-probability positive prediction -> DA/OT micro-rise.
26
+ * High-probability negative prediction -> CORT micro-rise.
27
+ */
28
+ export declare function generateAnticipation(predictions: StimulusPrediction[], currentChemistry: ChemicalState): AnticipationState;
29
+ /**
30
+ * Compute disappointment/surprise when actual stimulus differs from prediction.
31
+ * Returns additional chemistry delta beyond the normal stimulus response.
32
+ */
33
+ export declare function computeSurpriseEffect(anticipated: AnticipationState, actualStimulus: StimulusType | null): Partial<ChemicalState>;
34
+ /**
35
+ * Evaluate if the last interaction would have gone better with different chemistry.
36
+ * Runs a counterfactual: "what if my chemistry had been at baseline?"
37
+ */
38
+ export declare function computeRegret(preInteractionState: PsycheState, postInteractionState: PsycheState, outcomeScore: number, appliedStimulus: StimulusType | null): RegretEntry | null;
@@ -0,0 +1,276 @@
1
+ // ============================================================
2
+ // Temporal Consciousness — Anticipation, Surprise, and Regret
3
+ //
4
+ // Implements predictive emotional processing:
5
+ // 1. PredictiveModel — Markov-based stimulus prediction
6
+ // 2. AnticipationEngine — pre-stimulus chemistry shifts
7
+ // 3. RegretComputer — counterfactual outcome analysis
8
+ //
9
+ // These enable the agent to "look forward" and "look back",
10
+ // creating a temporal dimension to emotional experience.
11
+ // ============================================================
12
+ import { CHEMICAL_KEYS } from "./types.js";
13
+ import { STIMULUS_VECTORS } from "./chemistry.js";
14
+ // ── All StimulusType values ──────────────────────────────────
15
+ const ALL_STIMULI = [
16
+ "praise", "criticism", "humor", "intellectual", "intimacy",
17
+ "conflict", "neglect", "surprise", "casual",
18
+ "sarcasm", "authority", "validation", "boredom", "vulnerability",
19
+ ];
20
+ // ── Phase Priors ─────────────────────────────────────────────
21
+ // Default probability weights per relationship phase.
22
+ const PHASE_PRIORS = {
23
+ stranger: {
24
+ casual: 3, intellectual: 2, humor: 1.5, boredom: 1.5,
25
+ criticism: 0.5, intimacy: 0.3, vulnerability: 0.3,
26
+ praise: 1, validation: 0.8, surprise: 1, conflict: 0.5,
27
+ neglect: 1, sarcasm: 0.8, authority: 0.8,
28
+ },
29
+ acquaintance: {
30
+ casual: 2.5, intellectual: 2, humor: 2, praise: 1.5,
31
+ validation: 1.2, surprise: 1, criticism: 0.8, conflict: 0.5,
32
+ intimacy: 0.5, vulnerability: 0.5, neglect: 0.8,
33
+ sarcasm: 0.8, authority: 0.8, boredom: 1,
34
+ },
35
+ familiar: {
36
+ casual: 2, humor: 2.5, praise: 2, validation: 2,
37
+ intellectual: 2, intimacy: 1.5, vulnerability: 1,
38
+ surprise: 1.2, criticism: 1, conflict: 0.8, neglect: 0.6,
39
+ sarcasm: 1, authority: 0.6, boredom: 0.8,
40
+ },
41
+ close: {
42
+ intimacy: 3, humor: 2.5, validation: 2.5, praise: 2,
43
+ vulnerability: 2, casual: 2, intellectual: 1.5,
44
+ surprise: 1.5, criticism: 1, conflict: 0.8, neglect: 0.5,
45
+ sarcasm: 0.8, authority: 0.5, boredom: 0.5,
46
+ },
47
+ deep: {
48
+ intimacy: 3.5, vulnerability: 3, validation: 2.5, humor: 2.5,
49
+ praise: 2, casual: 1.5, intellectual: 2, surprise: 1.5,
50
+ criticism: 1, conflict: 0.8, neglect: 0.3, sarcasm: 0.5,
51
+ authority: 0.3, boredom: 0.3,
52
+ },
53
+ };
54
+ // ── 1. PredictiveModel ──────────────────────────────────────
55
+ /**
56
+ * Predict likely next stimulus based on interaction history.
57
+ * Uses simple Markov property: given recent stimulus sequence, what comes next?
58
+ */
59
+ export function predictNextStimulus(emotionalHistory, relationshipPhase) {
60
+ const phasePrior = PHASE_PRIORS[relationshipPhase] ?? PHASE_PRIORS.acquaintance;
61
+ // Insufficient history: return flat prior weighted by phase
62
+ if (emotionalHistory.length < 3) {
63
+ return buildPhasePrior(phasePrior);
64
+ }
65
+ // Extract the last 2 stimuli for bigram transition
66
+ const recent = emotionalHistory.slice(-2);
67
+ const lastTwo = recent.map((s) => s.stimulus).filter((s) => s !== null);
68
+ if (lastTwo.length < 2) {
69
+ return buildPhasePrior(phasePrior);
70
+ }
71
+ // Build transition counts from history (all consecutive pairs)
72
+ const transitionCounts = new Map();
73
+ for (let i = 1; i < emotionalHistory.length; i++) {
74
+ const prev = emotionalHistory[i - 1].stimulus;
75
+ const cur = emotionalHistory[i].stimulus;
76
+ if (prev === null || cur === null)
77
+ continue;
78
+ const key = prev;
79
+ if (!transitionCounts.has(key)) {
80
+ transitionCounts.set(key, new Map());
81
+ }
82
+ const counts = transitionCounts.get(key);
83
+ counts.set(cur, (counts.get(cur) ?? 0) + 1);
84
+ }
85
+ // Get transition probabilities from the last stimulus
86
+ const lastStimulus = lastTwo[lastTwo.length - 1];
87
+ const transitions = transitionCounts.get(lastStimulus);
88
+ // If no transitions observed from this stimulus, fall back to phase prior
89
+ if (!transitions || transitions.size === 0) {
90
+ return buildPhasePrior(phasePrior);
91
+ }
92
+ // Merge Markov transitions with phase prior (50/50 blend)
93
+ let totalTransitions = 0;
94
+ for (const count of transitions.values()) {
95
+ totalTransitions += count;
96
+ }
97
+ const predictions = [];
98
+ let totalWeight = 0;
99
+ for (const stim of ALL_STIMULI) {
100
+ const markovProb = totalTransitions > 0
101
+ ? (transitions.get(stim) ?? 0) / totalTransitions
102
+ : 0;
103
+ const priorWeight = phasePrior[stim] ?? 0.5;
104
+ // Blend: 50% Markov, 50% phase prior (normalized)
105
+ const combined = markovProb * 0.5 + (priorWeight / 20) * 0.5;
106
+ totalWeight += combined;
107
+ predictions.push({ stimulus: stim, probability: combined });
108
+ }
109
+ // Normalize
110
+ if (totalWeight > 0) {
111
+ for (const p of predictions) {
112
+ p.probability = p.probability / totalWeight;
113
+ }
114
+ }
115
+ // Sort by probability descending
116
+ predictions.sort((a, b) => b.probability - a.probability);
117
+ return predictions;
118
+ }
119
+ /**
120
+ * Build a flat phase-weighted prior distribution.
121
+ */
122
+ function buildPhasePrior(weights) {
123
+ let totalWeight = 0;
124
+ const predictions = [];
125
+ for (const stim of ALL_STIMULI) {
126
+ const w = weights[stim] ?? 0.5;
127
+ totalWeight += w;
128
+ predictions.push({ stimulus: stim, probability: w });
129
+ }
130
+ // Normalize
131
+ if (totalWeight > 0) {
132
+ for (const p of predictions) {
133
+ p.probability = p.probability / totalWeight;
134
+ }
135
+ }
136
+ predictions.sort((a, b) => b.probability - a.probability);
137
+ return predictions;
138
+ }
139
+ // ── 2. AnticipationEngine ───────────────────────────────────
140
+ /**
141
+ * Generate anticipatory chemistry changes based on predictions.
142
+ * High-probability positive prediction -> DA/OT micro-rise.
143
+ * High-probability negative prediction -> CORT micro-rise.
144
+ */
145
+ export function generateAnticipation(predictions, currentChemistry) {
146
+ const anticipation = {};
147
+ for (const key of CHEMICAL_KEYS) {
148
+ anticipation[key] = 0;
149
+ }
150
+ // For each prediction with probability > 0.2, compute micro shift
151
+ for (const pred of predictions) {
152
+ if (pred.probability <= 0.2)
153
+ continue;
154
+ const vector = STIMULUS_VECTORS[pred.stimulus];
155
+ if (!vector)
156
+ continue;
157
+ const scale = 0.15 * pred.probability;
158
+ for (const key of CHEMICAL_KEYS) {
159
+ anticipation[key] += vector[key] * scale;
160
+ }
161
+ }
162
+ // Clamp total anticipation shift to +/-5 per chemical
163
+ const clamped = {};
164
+ for (const key of CHEMICAL_KEYS) {
165
+ const val = Math.max(-5, Math.min(5, anticipation[key]));
166
+ if (Math.abs(val) > 0.01) {
167
+ clamped[key] = Math.round(val * 100) / 100;
168
+ }
169
+ }
170
+ return {
171
+ predictions,
172
+ anticipatoryChemistry: clamped,
173
+ timestamp: new Date().toISOString(),
174
+ };
175
+ }
176
+ /**
177
+ * Compute disappointment/surprise when actual stimulus differs from prediction.
178
+ * Returns additional chemistry delta beyond the normal stimulus response.
179
+ */
180
+ export function computeSurpriseEffect(anticipated, actualStimulus) {
181
+ if (!actualStimulus || anticipated.predictions.length === 0) {
182
+ return {};
183
+ }
184
+ const topPrediction = anticipated.predictions[0];
185
+ const topConfidence = topPrediction.probability;
186
+ // Find the predicted probability for the actual stimulus
187
+ const actualPrediction = anticipated.predictions.find((p) => p.stimulus === actualStimulus);
188
+ const actualProbability = actualPrediction?.probability ?? 0;
189
+ // If actual matches top prediction, no surprise
190
+ if (actualStimulus === topPrediction.stimulus) {
191
+ return {};
192
+ }
193
+ // Determine if the actual stimulus is positive or negative
194
+ const actualVector = STIMULUS_VECTORS[actualStimulus];
195
+ if (!actualVector)
196
+ return {};
197
+ const actualValence = actualVector.DA + actualVector.HT + actualVector.OT - actualVector.CORT;
198
+ const topVector = STIMULUS_VECTORS[topPrediction.stimulus];
199
+ const topValence = topVector
200
+ ? topVector.DA + topVector.HT + topVector.OT - topVector.CORT
201
+ : 0;
202
+ // Surprise magnitude scales with: (1) how confident the prediction was, (2) how unexpected the actual is
203
+ const surpriseMagnitude = topConfidence * (1 - actualProbability);
204
+ if (actualValence > 0 && topValence <= actualValence) {
205
+ // Pleasant surprise: actual is more positive than expected
206
+ return {
207
+ DA: Math.round(5 * surpriseMagnitude * 100) / 100,
208
+ END: Math.round(3 * surpriseMagnitude * 100) / 100,
209
+ };
210
+ }
211
+ else if (actualValence < topValence) {
212
+ // Disappointment: actual is worse than expected (the "crash" from anticipated warmth)
213
+ return {
214
+ DA: Math.round(-5 * surpriseMagnitude * 100) / 100,
215
+ CORT: Math.round(5 * surpriseMagnitude * 100) / 100,
216
+ };
217
+ }
218
+ return {};
219
+ }
220
+ // ── 3. RegretComputer ───────────────────────────────────────
221
+ /** Chemical descriptions for regret messages */
222
+ const CHEMICAL_DESCRIPTIONS = {
223
+ DA: { high: "high dopamine made response too eager", low: "low dopamine made response flat" },
224
+ HT: { high: "high serotonin made response complacent", low: "low serotonin made response unstable" },
225
+ CORT: { high: "high CORT made response too defensive", low: "low CORT made response careless" },
226
+ OT: { high: "high oxytocin made response too trusting", low: "low oxytocin made response too cold" },
227
+ NE: { high: "high norepinephrine made response too reactive", low: "low norepinephrine made response sluggish" },
228
+ END: { high: "high endorphins made response too flippant", low: "low endorphins made response too serious" },
229
+ };
230
+ /**
231
+ * Evaluate if the last interaction would have gone better with different chemistry.
232
+ * Runs a counterfactual: "what if my chemistry had been at baseline?"
233
+ */
234
+ export function computeRegret(preInteractionState, postInteractionState, outcomeScore, appliedStimulus) {
235
+ // Only generate regret for bad outcomes
236
+ if (outcomeScore >= -0.2) {
237
+ return null;
238
+ }
239
+ const baseline = preInteractionState.baseline;
240
+ const preChemistry = preInteractionState.current;
241
+ // Check if chemistry was significantly deviated from baseline
242
+ let maxDeviation = 0;
243
+ let mostDeviatedKey = "DA";
244
+ for (const key of CHEMICAL_KEYS) {
245
+ const deviation = Math.abs(preChemistry[key] - baseline[key]);
246
+ if (deviation > maxDeviation) {
247
+ maxDeviation = deviation;
248
+ mostDeviatedKey = key;
249
+ }
250
+ }
251
+ // No regret if chemistry was near baseline (deviation < 15)
252
+ if (maxDeviation < 15) {
253
+ return null;
254
+ }
255
+ // Compute regret intensity: |outcomeScore| * (maxDeviation / 100)
256
+ const regretIntensity = Math.min(1, Math.abs(outcomeScore) * (maxDeviation / 100));
257
+ // Build counterfactual delta: difference between baseline and actual pre-interaction chemistry
258
+ const counterfactualDelta = {};
259
+ for (const key of CHEMICAL_KEYS) {
260
+ const diff = baseline[key] - preChemistry[key];
261
+ if (Math.abs(diff) > 5) {
262
+ counterfactualDelta[key] = Math.round(diff * 100) / 100;
263
+ }
264
+ }
265
+ // Build description identifying the most deviated chemical
266
+ const deviationDirection = preChemistry[mostDeviatedKey] > baseline[mostDeviatedKey]
267
+ ? "high" : "low";
268
+ const description = CHEMICAL_DESCRIPTIONS[mostDeviatedKey][deviationDirection];
269
+ return {
270
+ turnIndex: postInteractionState.meta.totalInteractions,
271
+ counterfactualDelta,
272
+ regretIntensity,
273
+ description,
274
+ timestamp: new Date().toISOString(),
275
+ };
276
+ }
package/dist/types.d.ts CHANGED
@@ -49,12 +49,27 @@ export interface EmotionPattern {
49
49
  expressionHint: string;
50
50
  behaviorGuide: string;
51
51
  }
52
+ /** Attachment style for relationship dynamics */
53
+ export type AttachmentStyle = "secure" | "anxious" | "avoidant" | "disorganized";
54
+ /** Attachment state tracked per-relationship */
55
+ export interface AttachmentData {
56
+ style: AttachmentStyle;
57
+ strength: number;
58
+ securityScore: number;
59
+ anxietyScore: number;
60
+ avoidanceScore: number;
61
+ lastInteractionAt: string;
62
+ interactionCount: number;
63
+ }
64
+ /** Default attachment for new relationships */
65
+ export declare const DEFAULT_ATTACHMENT: AttachmentData;
52
66
  /** Relationship tracking */
53
67
  export interface RelationshipState {
54
68
  trust: number;
55
69
  intimacy: number;
56
70
  phase: "stranger" | "acquaintance" | "familiar" | "close" | "deep";
57
71
  memory?: string[];
72
+ attachment?: AttachmentData;
58
73
  }
59
74
  /** Chemical state snapshot for emotional memory */
60
75
  export interface ChemicalSnapshot {
@@ -128,9 +143,41 @@ export declare const MAX_LEARNED_VECTORS = 200;
128
143
  export declare const MAX_PREDICTION_HISTORY = 50;
129
144
  /** Max outcome history entries */
130
145
  export declare const MAX_OUTCOME_HISTORY = 50;
131
- /** Persisted psyche state for an agent (v4: emotional learning) */
146
+ /** Max regret history entries */
147
+ export declare const MAX_REGRET_HISTORY = 20;
148
+ /** Max metacognitive regulation history entries */
149
+ export declare const MAX_REGULATION_HISTORY = 30;
150
+ /** Max defense pattern entries */
151
+ export declare const MAX_DEFENSE_PATTERNS = 10;
152
+ /** Regulation strategy type */
153
+ export type RegulationStrategyType = "reappraisal" | "strategic-expression" | "self-soothing";
154
+ /** Defense mechanism type */
155
+ export type DefenseMechanismType = "rationalization" | "projection" | "sublimation" | "avoidance";
156
+ /** Record of a past regulation attempt */
157
+ export interface RegulationRecord {
158
+ strategy: RegulationStrategyType;
159
+ timestamp: string;
160
+ effective: boolean;
161
+ }
162
+ /** Tracked defense pattern frequency */
163
+ export interface DefensePatternRecord {
164
+ mechanism: DefenseMechanismType;
165
+ frequency: number;
166
+ lastSeen: string;
167
+ }
168
+ /** Persistent metacognitive state */
169
+ export interface MetacognitiveState {
170
+ regulationHistory: RegulationRecord[];
171
+ defensePatterns: DefensePatternRecord[];
172
+ /** Running average of emotional confidence across assessments */
173
+ avgEmotionalConfidence: number;
174
+ totalAssessments: number;
175
+ }
176
+ /** Default empty metacognitive state */
177
+ export declare const DEFAULT_METACOGNITIVE_STATE: MetacognitiveState;
178
+ /** Persisted psyche state for an agent (v5: metacognition + decision modulation) */
132
179
  export interface PsycheState {
133
- version: 3 | 4;
180
+ version: 3 | 4 | 5;
134
181
  mbti: MBTIType;
135
182
  baseline: ChemicalState;
136
183
  current: ChemicalState;
@@ -143,6 +190,7 @@ export interface PsycheState {
143
190
  agreementStreak: number;
144
191
  lastDisagreement: string | null;
145
192
  learning: LearningState;
193
+ metacognition: MetacognitiveState;
146
194
  meta: {
147
195
  agentName: string;
148
196
  createdAt: string;
package/dist/types.js CHANGED
@@ -56,6 +56,16 @@ export const CHEMICAL_DECAY_SPEED = {
56
56
  NE: "fast",
57
57
  END: "fast",
58
58
  };
59
+ /** Default attachment for new relationships */
60
+ export const DEFAULT_ATTACHMENT = {
61
+ style: "secure",
62
+ strength: 0,
63
+ securityScore: 50,
64
+ anxietyScore: 50,
65
+ avoidanceScore: 50,
66
+ lastInteractionAt: new Date().toISOString(),
67
+ interactionCount: 0,
68
+ };
59
69
  /** Max history entries to keep */
60
70
  export const MAX_EMOTIONAL_HISTORY = 10;
61
71
  /** Max compressed session memories per relationship */
@@ -73,6 +83,19 @@ export const MAX_LEARNED_VECTORS = 200;
73
83
  export const MAX_PREDICTION_HISTORY = 50;
74
84
  /** Max outcome history entries */
75
85
  export const MAX_OUTCOME_HISTORY = 50;
86
+ /** Max regret history entries */
87
+ export const MAX_REGRET_HISTORY = 20;
88
+ /** Max metacognitive regulation history entries */
89
+ export const MAX_REGULATION_HISTORY = 30;
90
+ /** Max defense pattern entries */
91
+ export const MAX_DEFENSE_PATTERNS = 10;
92
+ /** Default empty metacognitive state */
93
+ export const DEFAULT_METACOGNITIVE_STATE = {
94
+ regulationHistory: [],
95
+ defensePatterns: [],
96
+ avgEmotionalConfidence: 0.5,
97
+ totalAssessments: 0,
98
+ };
76
99
  /** Default relationship for new users */
77
100
  export const DEFAULT_RELATIONSHIP = {
78
101
  trust: 50,
package/dist/update.js CHANGED
@@ -11,7 +11,7 @@ import { execFile } from "node:child_process";
11
11
  import { promisify } from "node:util";
12
12
  const execFileAsync = promisify(execFile);
13
13
  const PACKAGE_NAME = "psyche-ai";
14
- const CURRENT_VERSION = "3.0.0";
14
+ const CURRENT_VERSION = "4.0.0";
15
15
  const CHECK_INTERVAL_MS = 60 * 60 * 1000; // 1 hour
16
16
  const CACHE_DIR = join(homedir(), ".psyche-ai");
17
17
  const CACHE_FILE = join(CACHE_DIR, "update-check.json");
@@ -2,7 +2,7 @@
2
2
  "id": "psyche-ai",
3
3
  "name": "Artificial Psyche",
4
4
  "description": "Virtual endocrine system, empathy engine, and agency for OpenClaw agents",
5
- "version": "3.0.0",
5
+ "version": "4.0.0",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "psyche-ai",
3
- "version": "3.0.0",
3
+ "version": "4.0.0",
4
4
  "description": "Artificial Psyche — universal emotional intelligence plugin for any AI agent",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",