psyche-ai 5.0.0 → 7.1.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 +67 -0
- package/dist/adapters/openclaw.js +7 -0
- package/dist/autonomic.d.ts +41 -0
- package/dist/autonomic.js +186 -0
- package/dist/circadian.d.ts +37 -0
- package/dist/circadian.js +97 -0
- package/dist/classify.d.ts +29 -2
- package/dist/classify.js +339 -53
- package/dist/cli.js +132 -13
- package/dist/core.d.ts +31 -2
- package/dist/core.js +246 -30
- package/dist/i18n.js +14 -0
- package/dist/index.d.ts +13 -5
- package/dist/index.js +11 -4
- package/dist/primary-systems.d.ts +55 -0
- package/dist/primary-systems.js +218 -0
- package/dist/profiles.d.ts +12 -1
- package/dist/profiles.js +42 -0
- package/dist/prompt.d.ts +26 -1
- package/dist/prompt.js +331 -33
- package/dist/psyche-file.d.ts +15 -1
- package/dist/psyche-file.js +147 -5
- package/dist/types.d.ts +16 -1
- package/dist/update.js +1 -1
- package/openclaw.plugin.json +35 -1
- package/package.json +4 -2
package/dist/psyche-file.js
CHANGED
|
@@ -9,6 +9,7 @@ import { getBaseline, getDefaultSelfModel, extractMBTI, getSensitivity, getTempe
|
|
|
9
9
|
import { applyDecay, detectEmotions } from "./chemistry.js";
|
|
10
10
|
import { decayDrives, computeEffectiveBaseline } from "./drives.js";
|
|
11
11
|
import { t } from "./i18n.js";
|
|
12
|
+
import { computeSelfReflection } from "./self-recognition.js";
|
|
12
13
|
const STATE_FILE = "psyche-state.json";
|
|
13
14
|
const PSYCHE_MD = "PSYCHE.md";
|
|
14
15
|
const IDENTITY_MD = "IDENTITY.md";
|
|
@@ -176,6 +177,127 @@ export function getRelationship(state, userId) {
|
|
|
176
177
|
const key = userId ?? "_default";
|
|
177
178
|
return state.relationships[key] ?? { ...DEFAULT_RELATIONSHIP };
|
|
178
179
|
}
|
|
180
|
+
// ── Tendency display labels ────────────────────────────────────
|
|
181
|
+
const TENDENCY_LABEL_ZH = {
|
|
182
|
+
ascending: "上扬",
|
|
183
|
+
descending: "下沉",
|
|
184
|
+
volatile: "波动",
|
|
185
|
+
oscillating: "起伏",
|
|
186
|
+
stable: "平稳",
|
|
187
|
+
};
|
|
188
|
+
const TENDENCY_LABEL_EN = {
|
|
189
|
+
ascending: "ascending",
|
|
190
|
+
descending: "descending",
|
|
191
|
+
volatile: "volatile",
|
|
192
|
+
oscillating: "oscillating",
|
|
193
|
+
stable: "stable",
|
|
194
|
+
};
|
|
195
|
+
/**
|
|
196
|
+
* Compress the full emotionalHistory into a rich session summary and store it
|
|
197
|
+
* in the user's relationship.memory[]. Called ONCE at session end.
|
|
198
|
+
*
|
|
199
|
+
* Pure computation, no LLM calls.
|
|
200
|
+
*/
|
|
201
|
+
export function compressSession(state, userId) {
|
|
202
|
+
const history = state.emotionalHistory ?? [];
|
|
203
|
+
// Need at least 2 entries for a meaningful summary
|
|
204
|
+
if (history.length < 2)
|
|
205
|
+
return state;
|
|
206
|
+
const locale = state.meta.locale ?? "zh";
|
|
207
|
+
const isZh = locale === "zh";
|
|
208
|
+
const first = history[0];
|
|
209
|
+
const last = history[history.length - 1];
|
|
210
|
+
// ── Date range ──
|
|
211
|
+
const d1 = new Date(first.timestamp);
|
|
212
|
+
const d2 = new Date(last.timestamp);
|
|
213
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
214
|
+
const dateRange = isZh
|
|
215
|
+
? `${d1.getMonth() + 1}月${d1.getDate()}日 ${pad(d1.getHours())}:${pad(d1.getMinutes())}-${pad(d2.getHours())}:${pad(d2.getMinutes())}`
|
|
216
|
+
: `${d1.getMonth() + 1}/${d1.getDate()} ${pad(d1.getHours())}:${pad(d1.getMinutes())}-${pad(d2.getHours())}:${pad(d2.getMinutes())}`;
|
|
217
|
+
// ── Turn count ──
|
|
218
|
+
const turnCount = history.length;
|
|
219
|
+
// ── Stimulus distribution ──
|
|
220
|
+
const stimuliCounts = {};
|
|
221
|
+
for (const snap of history) {
|
|
222
|
+
if (snap.stimulus) {
|
|
223
|
+
stimuliCounts[snap.stimulus] = (stimuliCounts[snap.stimulus] || 0) + 1;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const stimuliStr = Object.entries(stimuliCounts)
|
|
227
|
+
.sort((a, b) => b[1] - a[1])
|
|
228
|
+
.map(([type, count]) => `${type}×${count}`)
|
|
229
|
+
.join(",");
|
|
230
|
+
// ── Chemical trajectory ──
|
|
231
|
+
const trajectoryParts = [];
|
|
232
|
+
for (const key of CHEMICAL_KEYS) {
|
|
233
|
+
const delta = last.chemistry[key] - first.chemistry[key];
|
|
234
|
+
if (Math.abs(delta) > 10) {
|
|
235
|
+
trajectoryParts.push(`${key}${Math.round(first.chemistry[key])}→${Math.round(last.chemistry[key])}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
// ── Emotion arc ──
|
|
239
|
+
const emotions = [];
|
|
240
|
+
for (const snap of history) {
|
|
241
|
+
if (snap.dominantEmotion && (emotions.length === 0 || emotions[emotions.length - 1] !== snap.dominantEmotion)) {
|
|
242
|
+
emotions.push(snap.dominantEmotion);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const emotionArc = emotions.join("→");
|
|
246
|
+
// ── Peak event ──
|
|
247
|
+
let peakIdx = 0;
|
|
248
|
+
let peakDeviation = 0;
|
|
249
|
+
for (let i = 0; i < history.length; i++) {
|
|
250
|
+
let deviation = 0;
|
|
251
|
+
for (const key of CHEMICAL_KEYS) {
|
|
252
|
+
deviation += Math.abs(history[i].chemistry[key] - state.baseline[key]);
|
|
253
|
+
}
|
|
254
|
+
if (deviation > peakDeviation) {
|
|
255
|
+
peakDeviation = deviation;
|
|
256
|
+
peakIdx = i;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const peakSnap = history[peakIdx];
|
|
260
|
+
const peakLabel = isZh
|
|
261
|
+
? `第${peakIdx + 1}轮:${peakSnap.stimulus ?? "?"}→${peakSnap.dominantEmotion ?? "?"}`
|
|
262
|
+
: `turn${peakIdx + 1}:${peakSnap.stimulus ?? "?"}→${peakSnap.dominantEmotion ?? "?"}`;
|
|
263
|
+
// ── Tendency ──
|
|
264
|
+
const reflection = computeSelfReflection(history, locale);
|
|
265
|
+
const tendencyLabel = isZh
|
|
266
|
+
? (TENDENCY_LABEL_ZH[reflection.tendency] ?? reflection.tendency)
|
|
267
|
+
: (TENDENCY_LABEL_EN[reflection.tendency] ?? reflection.tendency);
|
|
268
|
+
// ── Build summary string ──
|
|
269
|
+
const turnsLabel = isZh ? "轮" : "turns";
|
|
270
|
+
const stimLabel = isZh ? "刺激" : "stimuli";
|
|
271
|
+
const trajLabel = isZh ? "轨迹" : "trajectory";
|
|
272
|
+
const arcLabel = isZh ? "弧线" : "arc";
|
|
273
|
+
const peakEventLabel = isZh ? "高峰" : "peak";
|
|
274
|
+
const tendLabel = isZh ? "倾向" : "tendency";
|
|
275
|
+
let summary = `${dateRange}(${turnCount}${turnsLabel})`;
|
|
276
|
+
if (stimuliStr)
|
|
277
|
+
summary += `: ${stimLabel}[${stimuliStr}]`;
|
|
278
|
+
if (trajectoryParts.length > 0)
|
|
279
|
+
summary += ` ${trajLabel}[${trajectoryParts.join(" ")}]`;
|
|
280
|
+
if (emotionArc)
|
|
281
|
+
summary += ` ${arcLabel}[${emotionArc}]`;
|
|
282
|
+
summary += ` ${peakEventLabel}[${peakLabel}]`;
|
|
283
|
+
summary += ` ${tendLabel}[${tendencyLabel}]`;
|
|
284
|
+
// ── Store in relationship memory ──
|
|
285
|
+
const relKey = userId ?? "_default";
|
|
286
|
+
const existing = state.relationships[relKey] ?? { ...DEFAULT_RELATIONSHIP };
|
|
287
|
+
const memory = [...(existing.memory ?? [])];
|
|
288
|
+
memory.push(summary);
|
|
289
|
+
if (memory.length > MAX_RELATIONSHIP_MEMORY) {
|
|
290
|
+
memory.splice(0, memory.length - MAX_RELATIONSHIP_MEMORY);
|
|
291
|
+
}
|
|
292
|
+
const updatedRel = { ...existing, memory };
|
|
293
|
+
const updatedRelationships = { ...state.relationships, [relKey]: updatedRel };
|
|
294
|
+
// ── Clear history and return ──
|
|
295
|
+
return {
|
|
296
|
+
...state,
|
|
297
|
+
emotionalHistory: [],
|
|
298
|
+
relationships: updatedRelationships,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
179
301
|
/**
|
|
180
302
|
* Load psyche state from workspace. Auto-initializes if missing.
|
|
181
303
|
* Handles v1→v2 migration transparently.
|
|
@@ -285,6 +407,7 @@ export async function initializeState(workspaceDir, opts, logger = NOOP_LOGGER)
|
|
|
285
407
|
createdAt: now,
|
|
286
408
|
totalInteractions: 0,
|
|
287
409
|
locale,
|
|
410
|
+
mode: "natural",
|
|
288
411
|
},
|
|
289
412
|
};
|
|
290
413
|
await saveState(workspaceDir, state);
|
|
@@ -323,6 +446,7 @@ export async function decayAndSave(workspaceDir, state) {
|
|
|
323
446
|
/**
|
|
324
447
|
* Parse a <psyche_update> block from LLM output.
|
|
325
448
|
* v0.2: supports decimals, Chinese names, English names.
|
|
449
|
+
* v2.1: supports LLM-assisted stimulus classification.
|
|
326
450
|
*/
|
|
327
451
|
export function parsePsycheUpdate(text, logger = NOOP_LOGGER) {
|
|
328
452
|
const match = text.match(/<psyche_update>([\s\S]*?)<\/psyche_update>/);
|
|
@@ -361,20 +485,34 @@ export function parsePsycheUpdate(text, logger = NOOP_LOGGER) {
|
|
|
361
485
|
timestamp: new Date().toISOString(),
|
|
362
486
|
};
|
|
363
487
|
}
|
|
488
|
+
// Parse LLM-assisted stimulus classification
|
|
489
|
+
let llmStimulus;
|
|
490
|
+
const stimulusMatch = block.match(/(?:stimulus|刺激类型)\s*[::]\s*(\w+)/i);
|
|
491
|
+
if (stimulusMatch) {
|
|
492
|
+
const candidate = stimulusMatch[1].trim().toLowerCase();
|
|
493
|
+
const VALID_STIMULI = new Set([
|
|
494
|
+
"praise", "criticism", "humor", "intellectual", "intimacy", "conflict",
|
|
495
|
+
"neglect", "surprise", "casual", "sarcasm", "authority", "validation",
|
|
496
|
+
"boredom", "vulnerability",
|
|
497
|
+
]);
|
|
498
|
+
if (VALID_STIMULI.has(candidate)) {
|
|
499
|
+
llmStimulus = candidate;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
364
502
|
// Parse relationship updates
|
|
365
503
|
const trustMatch = block.match(/(?:信任度|trust)\s*[::]\s*(\d+)/i);
|
|
366
504
|
const intimacyMatch = block.match(/(?:亲密度|intimacy)\s*[::]\s*(\d+)/i);
|
|
367
|
-
if (Object.keys(updates).length === 0 && !empathyLog && !trustMatch) {
|
|
505
|
+
if (Object.keys(updates).length === 0 && !empathyLog && !trustMatch && !llmStimulus) {
|
|
368
506
|
logger.debug(t("log.parse_debug", "zh", { snippet: block.slice(0, 100) }));
|
|
369
507
|
return null;
|
|
370
508
|
}
|
|
371
|
-
const
|
|
509
|
+
const stateUpdates = {};
|
|
372
510
|
if (Object.keys(updates).length > 0) {
|
|
373
511
|
// Store as partial — will be merged field-by-field in mergeUpdates
|
|
374
|
-
|
|
512
|
+
stateUpdates.current = updates;
|
|
375
513
|
}
|
|
376
514
|
if (empathyLog) {
|
|
377
|
-
|
|
515
|
+
stateUpdates.empathyLog = empathyLog;
|
|
378
516
|
}
|
|
379
517
|
if (trustMatch || intimacyMatch) {
|
|
380
518
|
const rel = {};
|
|
@@ -383,7 +521,11 @@ export function parsePsycheUpdate(text, logger = NOOP_LOGGER) {
|
|
|
383
521
|
if (intimacyMatch)
|
|
384
522
|
rel.intimacy = Math.max(0, Math.min(100, parseInt(intimacyMatch[1], 10)));
|
|
385
523
|
// Store as relationships._default for merging
|
|
386
|
-
|
|
524
|
+
stateUpdates.relationships = { _default: rel };
|
|
525
|
+
}
|
|
526
|
+
const result = { state: stateUpdates };
|
|
527
|
+
if (llmStimulus) {
|
|
528
|
+
result.llmStimulus = llmStimulus;
|
|
387
529
|
}
|
|
388
530
|
return result;
|
|
389
531
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -33,6 +33,16 @@ export type DecaySpeed = "fast" | "medium" | "slow";
|
|
|
33
33
|
export declare const DECAY_FACTORS: Record<DecaySpeed, number>;
|
|
34
34
|
/** Which chemicals decay at which speed */
|
|
35
35
|
export declare const CHEMICAL_DECAY_SPEED: Record<keyof ChemicalState, DecaySpeed>;
|
|
36
|
+
/** Psyche operating mode */
|
|
37
|
+
export type PsycheMode = "natural" | "work" | "companion";
|
|
38
|
+
/** Big Five personality traits (0-100 each) */
|
|
39
|
+
export interface PersonalityTraits {
|
|
40
|
+
openness: number;
|
|
41
|
+
conscientiousness: number;
|
|
42
|
+
extraversion: number;
|
|
43
|
+
agreeableness: number;
|
|
44
|
+
neuroticism: number;
|
|
45
|
+
}
|
|
36
46
|
/** MBTI type string */
|
|
37
47
|
export type MBTIType = "INTJ" | "INTP" | "ENTJ" | "ENTP" | "INFJ" | "INFP" | "ENFJ" | "ENFP" | "ISTJ" | "ISFJ" | "ESTJ" | "ESFJ" | "ISTP" | "ISFP" | "ESTP" | "ESFP";
|
|
38
48
|
/** Stimulus types that affect chemistry (v0.2: +5 new types) */
|
|
@@ -211,7 +221,7 @@ export interface PersonhoodState {
|
|
|
211
221
|
export declare const DEFAULT_PERSONHOOD_STATE: PersonhoodState;
|
|
212
222
|
/** Persisted psyche state for an agent (v6: digital personhood) */
|
|
213
223
|
export interface PsycheState {
|
|
214
|
-
version: 3 | 4 | 5 | 6;
|
|
224
|
+
version: 3 | 4 | 5 | 6 | 7;
|
|
215
225
|
mbti: MBTIType;
|
|
216
226
|
baseline: ChemicalState;
|
|
217
227
|
current: ChemicalState;
|
|
@@ -226,11 +236,16 @@ export interface PsycheState {
|
|
|
226
236
|
learning: LearningState;
|
|
227
237
|
metacognition: MetacognitiveState;
|
|
228
238
|
personhood: PersonhoodState;
|
|
239
|
+
/** v7: autonomic nervous system state (Polyvagal Theory) */
|
|
240
|
+
autonomicState?: "ventral-vagal" | "sympathetic" | "dorsal-vagal";
|
|
241
|
+
/** v7: session start time for homeostatic pressure calculation */
|
|
242
|
+
sessionStartedAt?: string;
|
|
229
243
|
meta: {
|
|
230
244
|
agentName: string;
|
|
231
245
|
createdAt: string;
|
|
232
246
|
totalInteractions: number;
|
|
233
247
|
locale: Locale;
|
|
248
|
+
mode?: PsycheMode;
|
|
234
249
|
};
|
|
235
250
|
}
|
|
236
251
|
/** Default relationship for new users */
|
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 = "5.
|
|
14
|
+
const CURRENT_VERSION = "5.1.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
|
@@ -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": "5.
|
|
5
|
+
"version": "5.1.0",
|
|
6
6
|
"configSchema": {
|
|
7
7
|
"type": "object",
|
|
8
8
|
"additionalProperties": false,
|
|
@@ -31,6 +31,24 @@
|
|
|
31
31
|
"type": "boolean",
|
|
32
32
|
"default": true,
|
|
33
33
|
"description": "Compact mode: algorithms handle chemistry, LLM only sees behavioral instructions (~15-180 tokens vs ~550)"
|
|
34
|
+
},
|
|
35
|
+
"mode": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"enum": ["natural", "work", "companion"],
|
|
38
|
+
"default": "natural",
|
|
39
|
+
"description": "Operating mode: natural (balanced), work (minimal emotions), companion (full emotions)"
|
|
40
|
+
},
|
|
41
|
+
"personalityIntensity": {
|
|
42
|
+
"type": "number",
|
|
43
|
+
"default": 0.7,
|
|
44
|
+
"minimum": 0,
|
|
45
|
+
"maximum": 1,
|
|
46
|
+
"description": "Personality expression intensity (0=traditional AI, 1=full Psyche)"
|
|
47
|
+
},
|
|
48
|
+
"persist": {
|
|
49
|
+
"type": "boolean",
|
|
50
|
+
"default": true,
|
|
51
|
+
"description": "Persist emotional state to disk. Set false for privacy."
|
|
34
52
|
}
|
|
35
53
|
}
|
|
36
54
|
},
|
|
@@ -55,6 +73,22 @@
|
|
|
55
73
|
"label": "Compact Mode (Token Efficient)",
|
|
56
74
|
"sensitive": false,
|
|
57
75
|
"advanced": true
|
|
76
|
+
},
|
|
77
|
+
"mode": {
|
|
78
|
+
"label": "Operating Mode",
|
|
79
|
+
"sensitive": false,
|
|
80
|
+
"advanced": false
|
|
81
|
+
},
|
|
82
|
+
"personalityIntensity": {
|
|
83
|
+
"label": "Personality Intensity (0-1)",
|
|
84
|
+
"placeholder": "0.7",
|
|
85
|
+
"sensitive": false,
|
|
86
|
+
"advanced": true
|
|
87
|
+
},
|
|
88
|
+
"persist": {
|
|
89
|
+
"label": "Persist State",
|
|
90
|
+
"sensitive": false,
|
|
91
|
+
"advanced": true
|
|
58
92
|
}
|
|
59
93
|
}
|
|
60
94
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "psyche-ai",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.1.0",
|
|
4
4
|
"description": "Artificial Psyche — universal emotional intelligence plugin for any AI agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -36,7 +36,8 @@
|
|
|
36
36
|
"build:test": "tsc -p tsconfig.test.json",
|
|
37
37
|
"test": "npm run build && npm run build:test && node --test dist-test/tests/*.test.js",
|
|
38
38
|
"typecheck": "tsc --noEmit --strict",
|
|
39
|
-
"dev": "tsc --watch"
|
|
39
|
+
"dev": "tsc --watch",
|
|
40
|
+
"demo": "node scripts/demo-ab.js"
|
|
40
41
|
},
|
|
41
42
|
"license": "MIT",
|
|
42
43
|
"repository": {
|
|
@@ -86,6 +87,7 @@
|
|
|
86
87
|
]
|
|
87
88
|
},
|
|
88
89
|
"devDependencies": {
|
|
90
|
+
"@types/node": "^25.5.0",
|
|
89
91
|
"typescript": "^5.9.3"
|
|
90
92
|
}
|
|
91
93
|
}
|