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/README.md +6 -1
- package/dist/attachment.d.ts +30 -0
- package/dist/attachment.js +241 -0
- package/dist/cli.js +0 -0
- package/dist/core.js +39 -3
- package/dist/decision-bias.d.ts +58 -0
- package/dist/decision-bias.js +211 -0
- package/dist/index.d.ts +10 -2
- package/dist/index.js +9 -1
- package/dist/metacognition.d.ts +60 -0
- package/dist/metacognition.js +611 -0
- package/dist/prompt.d.ts +6 -1
- package/dist/prompt.js +26 -4
- package/dist/psyche-file.js +6 -3
- package/dist/temporal.d.ts +38 -0
- package/dist/temporal.js +276 -0
- package/dist/types.d.ts +50 -2
- package/dist/types.js +23 -0
- package/dist/update.js +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
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.
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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.`);
|
package/dist/psyche-file.js
CHANGED
|
@@ -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:
|
|
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:
|
|
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;
|
package/dist/temporal.js
ADDED
|
@@ -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
|
-
/**
|
|
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 = "
|
|
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");
|
package/openclaw.plugin.json
CHANGED