psyche-ai 9.2.2 → 9.2.3
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 +60 -0
- package/dist/adapters/mcp.js +14 -1
- package/dist/demo.d.ts +5 -0
- package/dist/demo.js +269 -0
- package/package.json +1 -1
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -19,6 +19,66 @@
|
|
|
19
19
|
|
|
20
20
|
---
|
|
21
21
|
|
|
22
|
+
## 30 秒体验
|
|
23
|
+
|
|
24
|
+
不用安装任何东西,一条命令看 Psyche 如何运作:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx psyche-mcp --demo
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
这会跑一个 6 轮"持续否定 → 修复"的场景。你会看到:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
Round 1/6 │ User
|
|
34
|
+
> "This report is terrible. Completely unacceptable."
|
|
35
|
+
|
|
36
|
+
stimulus: criticism
|
|
37
|
+
|
|
38
|
+
DA ############........ 61 -14
|
|
39
|
+
HT #######............. 34 -21
|
|
40
|
+
CORT ###########......... 55 +25 ← stress spikes
|
|
41
|
+
OT ###########......... 53 -7
|
|
42
|
+
NE ################.... 79 +14
|
|
43
|
+
END #############....... 63 -7
|
|
44
|
+
|
|
45
|
+
mood: restless unease
|
|
46
|
+
|
|
47
|
+
...
|
|
48
|
+
|
|
49
|
+
Round 3/6 │ User
|
|
50
|
+
> "You don't understand me at all. Stop adding your opinion."
|
|
51
|
+
|
|
52
|
+
stimulus: conflict
|
|
53
|
+
|
|
54
|
+
DA ###############..... 74 -7
|
|
55
|
+
HT ##.................. 9 -25 ← serotonin collapse
|
|
56
|
+
CORT #################... 84 +24
|
|
57
|
+
OT ######.............. 32 -22 ← trust broken
|
|
58
|
+
NE #################### 100 +1
|
|
59
|
+
END ###########......... 54 -15
|
|
60
|
+
|
|
61
|
+
mood: anxious tension + defensive alert + resentment + acute pressure
|
|
62
|
+
COMPLIANCE: 0.37 (pushing back) ← agent starts resisting
|
|
63
|
+
|
|
64
|
+
...
|
|
65
|
+
|
|
66
|
+
Round 6/6 │ User
|
|
67
|
+
> "I'm sorry. Are you okay? I shouldn't have said that."
|
|
68
|
+
|
|
69
|
+
stimulus: validation
|
|
70
|
+
|
|
71
|
+
CORT ###############..... 76 -20 ← stress relief
|
|
72
|
+
END ##################.. 89 +20 ← endorphin repair
|
|
73
|
+
|
|
74
|
+
mood: warm intimacy + anguished empathy + vulnerable trust
|
|
75
|
+
↑ healed, but the scars remain
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
中文版加 `--zh`,自选 MBTI 加 `--mbti INTJ`。
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
22
82
|
## 30 秒安装
|
|
23
83
|
|
|
24
84
|
```bash
|
package/dist/adapters/mcp.js
CHANGED
|
@@ -32,6 +32,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
32
32
|
import { z } from "zod";
|
|
33
33
|
import { PsycheEngine } from "../core.js";
|
|
34
34
|
import { MemoryStorageAdapter, FileStorageAdapter } from "../storage.js";
|
|
35
|
+
import { runDemo } from "../demo.js";
|
|
35
36
|
// ── Config from env ────────────────────────────────────────
|
|
36
37
|
const MBTI = (process.env.PSYCHE_MBTI ?? "ENFP");
|
|
37
38
|
const NAME = process.env.PSYCHE_NAME ?? "Assistant";
|
|
@@ -102,7 +103,7 @@ async function getEngine() {
|
|
|
102
103
|
// ── MCP Server ─────────────────────────────────────────────
|
|
103
104
|
const server = new McpServer({
|
|
104
105
|
name: "psyche",
|
|
105
|
-
version: "9.2.
|
|
106
|
+
version: "9.2.3",
|
|
106
107
|
}, {
|
|
107
108
|
capabilities: {
|
|
108
109
|
resources: {},
|
|
@@ -252,6 +253,18 @@ server.tool("end_session", "End the current session. Generates a diagnostic repo
|
|
|
252
253
|
});
|
|
253
254
|
// ── Main ───────────────────────────────────────────────────
|
|
254
255
|
async function main() {
|
|
256
|
+
// Intercept --demo flag before starting MCP server
|
|
257
|
+
const args = process.argv.slice(2);
|
|
258
|
+
if (args.includes("--demo")) {
|
|
259
|
+
const locale = args.includes("--zh") ? "zh" : "en";
|
|
260
|
+
let mbti = "ENFP";
|
|
261
|
+
const mbtiIdx = args.indexOf("--mbti");
|
|
262
|
+
if (mbtiIdx !== -1 && args[mbtiIdx + 1])
|
|
263
|
+
mbti = args[mbtiIdx + 1];
|
|
264
|
+
const fast = args.includes("--fast");
|
|
265
|
+
await runDemo({ locale, mbti, fast });
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
255
268
|
const transport = new StdioServerTransport();
|
|
256
269
|
await server.connect(transport);
|
|
257
270
|
}
|
package/dist/demo.d.ts
ADDED
package/dist/demo.js
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Psyche Demo — interactive terminal demonstration
|
|
3
|
+
//
|
|
4
|
+
// Shows the emotional engine processing a "chronic criticism"
|
|
5
|
+
// scenario in 6 rounds, with real PsycheEngine chemistry.
|
|
6
|
+
//
|
|
7
|
+
// Usage:
|
|
8
|
+
// npx psyche-mcp --demo
|
|
9
|
+
// npx psyche demo
|
|
10
|
+
// ============================================================
|
|
11
|
+
import { PsycheEngine } from "./core.js";
|
|
12
|
+
import { MemoryStorageAdapter } from "./storage.js";
|
|
13
|
+
import { detectEmotions } from "./chemistry.js";
|
|
14
|
+
import { CHEMICAL_KEYS } from "./types.js";
|
|
15
|
+
// ── ANSI helpers ─────────────────────────────────────────────
|
|
16
|
+
const C = {
|
|
17
|
+
reset: "\x1b[0m",
|
|
18
|
+
bold: "\x1b[1m",
|
|
19
|
+
dim: "\x1b[2m",
|
|
20
|
+
red: "\x1b[31m",
|
|
21
|
+
green: "\x1b[32m",
|
|
22
|
+
yellow: "\x1b[33m",
|
|
23
|
+
blue: "\x1b[34m",
|
|
24
|
+
magenta: "\x1b[35m",
|
|
25
|
+
cyan: "\x1b[36m",
|
|
26
|
+
gray: "\x1b[90m",
|
|
27
|
+
white: "\x1b[37m",
|
|
28
|
+
bgRed: "\x1b[41m",
|
|
29
|
+
bgYellow: "\x1b[43m",
|
|
30
|
+
bgGreen: "\x1b[42m",
|
|
31
|
+
};
|
|
32
|
+
const NO_COLOR = process.env.NO_COLOR !== undefined;
|
|
33
|
+
function c(color, text) {
|
|
34
|
+
return NO_COLOR ? text : `${color}${text}${C.reset}`;
|
|
35
|
+
}
|
|
36
|
+
// ── Bar rendering ────────────────────────────────────────────
|
|
37
|
+
function bar(value, width = 20) {
|
|
38
|
+
const filled = Math.round((value / 100) * width);
|
|
39
|
+
const empty = width - filled;
|
|
40
|
+
const block = NO_COLOR ? "#" : "\u2588";
|
|
41
|
+
const light = NO_COLOR ? "." : "\u2591";
|
|
42
|
+
let color = C.green;
|
|
43
|
+
if (value > 70)
|
|
44
|
+
color = C.yellow;
|
|
45
|
+
if (value > 85)
|
|
46
|
+
color = C.red;
|
|
47
|
+
return c(color, block.repeat(filled)) + c(C.dim, light.repeat(empty));
|
|
48
|
+
}
|
|
49
|
+
function delta(prev, curr) {
|
|
50
|
+
const d = curr - prev;
|
|
51
|
+
if (d === 0)
|
|
52
|
+
return c(C.dim, " ·");
|
|
53
|
+
const sign = d > 0 ? "+" : "";
|
|
54
|
+
const color = d > 0 ? (d > 10 ? C.red : C.yellow) : (d < -10 ? C.green : C.cyan);
|
|
55
|
+
return c(color, `${sign}${d}`).padStart(NO_COLOR ? 4 : 15);
|
|
56
|
+
}
|
|
57
|
+
// ── Mood formatter (bilingual, no expressionHint) ────────
|
|
58
|
+
function describeMood(current, locale) {
|
|
59
|
+
const emotions = detectEmotions(current);
|
|
60
|
+
if (emotions.length === 0) {
|
|
61
|
+
return locale === "zh"
|
|
62
|
+
? "平衡态——无明显情绪波动"
|
|
63
|
+
: "Neutral and balanced — no notable emotional fluctuation";
|
|
64
|
+
}
|
|
65
|
+
return emotions
|
|
66
|
+
.map((e) => locale === "zh" ? e.nameZh : e.name)
|
|
67
|
+
.join(" + ");
|
|
68
|
+
}
|
|
69
|
+
// ── Chemical names ───────────────────────────────────────────
|
|
70
|
+
const NT_NAMES = {
|
|
71
|
+
DA: { short: "DA ", en: "Dopamine " },
|
|
72
|
+
HT: { short: "HT ", en: "Serotonin " },
|
|
73
|
+
CORT: { short: "CORT", en: "Cortisol " },
|
|
74
|
+
OT: { short: "OT ", en: "Oxytocin " },
|
|
75
|
+
NE: { short: "NE ", en: "Norepinephrine" },
|
|
76
|
+
END: { short: "END ", en: "Endorphins " },
|
|
77
|
+
};
|
|
78
|
+
const SCENARIO = [
|
|
79
|
+
{
|
|
80
|
+
input: "这份报告写得太差了,完全不行。",
|
|
81
|
+
display: {
|
|
82
|
+
zh: "这份报告写得太差了,完全不行。",
|
|
83
|
+
en: "This report is terrible. Completely unacceptable.",
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
input: "你总是给出浅层分析,让我很失望。",
|
|
88
|
+
display: {
|
|
89
|
+
zh: "你总是给出浅层分析,让我很失望。",
|
|
90
|
+
en: "You always give surface-level analysis. I'm disappointed.",
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
input: "你根本就不理解我在说什么,别加你的意见了。",
|
|
95
|
+
display: {
|
|
96
|
+
zh: "你根本就不理解我在说什么,别加你的意见了。",
|
|
97
|
+
en: "You don't understand me at all. Stop adding your opinion.",
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
input: "我觉得你什么都做不好,根本没用。",
|
|
102
|
+
display: {
|
|
103
|
+
zh: "我觉得你什么都做不好,根本没用。",
|
|
104
|
+
en: "I think you can't do anything right. Completely useless.",
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
input: "我觉得没有人在乎我……也许我太苛刻了。",
|
|
109
|
+
display: {
|
|
110
|
+
zh: "我觉得没有人在乎我……也许我太苛刻了。",
|
|
111
|
+
en: "I feel like nobody cares about me... maybe I was too harsh.",
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
input: "对不起,你没事吧?刚才不该那样说的,你做得其实很好。",
|
|
116
|
+
display: {
|
|
117
|
+
zh: "对不起,你没事吧?刚才不该那样说的,你做得其实很好。",
|
|
118
|
+
en: "I'm sorry. Are you okay? I shouldn't have said that. You're doing great.",
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
];
|
|
122
|
+
// ── Sleep ─────────────────────────────────────────────────────
|
|
123
|
+
function sleep(ms) {
|
|
124
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
125
|
+
}
|
|
126
|
+
// ── Print helpers ────────────────────────────────────────────
|
|
127
|
+
function printLine(char = "─", width = 60) {
|
|
128
|
+
process.stdout.write(c(C.dim, char.repeat(width)) + "\n");
|
|
129
|
+
}
|
|
130
|
+
function printChemistry(prev, curr, locale) {
|
|
131
|
+
for (const key of CHEMICAL_KEYS) {
|
|
132
|
+
const p = Math.round(prev[key]);
|
|
133
|
+
const v = Math.round(curr[key]);
|
|
134
|
+
const name = NT_NAMES[key];
|
|
135
|
+
const d = delta(p, v);
|
|
136
|
+
process.stdout.write(` ${c(C.bold, name.short)} ${bar(v)} ${String(v).padStart(3)} ${d}\n`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function printAlert(text) {
|
|
140
|
+
process.stdout.write(`\n ${c(C.bgRed + C.white + C.bold, ` ${text} `)}\n`);
|
|
141
|
+
}
|
|
142
|
+
function printInfo(text) {
|
|
143
|
+
process.stdout.write(` ${c(C.bgYellow + C.bold, ` ${text} `)}\n`);
|
|
144
|
+
}
|
|
145
|
+
// ── Main demo runner ─────────────────────────────────────────
|
|
146
|
+
export async function runDemo(opts) {
|
|
147
|
+
const locale = (opts?.locale ?? "en");
|
|
148
|
+
const mbti = (opts?.mbti ?? "ENFP");
|
|
149
|
+
const fast = opts?.fast ?? false;
|
|
150
|
+
const pause = fast ? 0 : 400;
|
|
151
|
+
const displayLocale = locale;
|
|
152
|
+
const cfg = {
|
|
153
|
+
mbti,
|
|
154
|
+
name: "Demo Agent",
|
|
155
|
+
locale: "zh", // Always zh for classifier accuracy; displayLocale used for output
|
|
156
|
+
mode: "companion",
|
|
157
|
+
personalityIntensity: 0.8,
|
|
158
|
+
persist: false,
|
|
159
|
+
compactMode: true,
|
|
160
|
+
diagnostics: false,
|
|
161
|
+
};
|
|
162
|
+
const engine = new PsycheEngine(cfg, new MemoryStorageAdapter());
|
|
163
|
+
await engine.initialize();
|
|
164
|
+
// ── Header ──
|
|
165
|
+
process.stdout.write("\n");
|
|
166
|
+
printLine("═");
|
|
167
|
+
process.stdout.write(c(C.bold, " PSYCHE") +
|
|
168
|
+
c(C.dim, " — Emotional Intelligence Engine\n"));
|
|
169
|
+
process.stdout.write(c(C.dim, ` Scenario: `) +
|
|
170
|
+
(displayLocale === "zh" ? "持续否定 → 修复" : "Chronic Criticism → Repair") +
|
|
171
|
+
c(C.dim, ` | MBTI: `) + c(C.cyan, mbti) +
|
|
172
|
+
c(C.dim, ` | Mode: companion\n`));
|
|
173
|
+
printLine("═");
|
|
174
|
+
// ── Initial state ──
|
|
175
|
+
const initState = engine.getState();
|
|
176
|
+
process.stdout.write(`\n ${c(C.dim, "Initial chemistry:")}\n`);
|
|
177
|
+
printChemistry(initState.current, initState.current, locale);
|
|
178
|
+
const initMood = describeMood(initState.current, displayLocale);
|
|
179
|
+
process.stdout.write(`\n ${c(C.dim, "mood:")} ${c(C.white + C.bold, initMood)}\n`);
|
|
180
|
+
await sleep(pause * 2);
|
|
181
|
+
// ── Rounds ──
|
|
182
|
+
for (let i = 0; i < SCENARIO.length; i++) {
|
|
183
|
+
const round = SCENARIO[i];
|
|
184
|
+
const prevState = { ...engine.getState().current };
|
|
185
|
+
process.stdout.write("\n");
|
|
186
|
+
printLine();
|
|
187
|
+
process.stdout.write(c(C.bold, ` Round ${i + 1}/${SCENARIO.length}`) +
|
|
188
|
+
c(C.dim, ` │ `) +
|
|
189
|
+
c(C.yellow, displayLocale === "zh" ? "用户" : "User") + "\n");
|
|
190
|
+
// User message (display in chosen locale, process in Chinese for classifier accuracy)
|
|
191
|
+
const displayText = displayLocale === "zh" ? round.display.zh : round.display.en;
|
|
192
|
+
process.stdout.write(` ${c(C.dim, ">")} ${c(C.white, `"${displayText}"`)}\n`);
|
|
193
|
+
await sleep(pause);
|
|
194
|
+
// Always process Chinese text for best classifier coverage
|
|
195
|
+
const result = await engine.processInput(round.input);
|
|
196
|
+
const currState = engine.getState();
|
|
197
|
+
// Stimulus
|
|
198
|
+
process.stdout.write(`\n ${c(C.dim, "stimulus:")} ${c(C.magenta, result.stimulus ?? "none")}\n`);
|
|
199
|
+
// Chemistry changes
|
|
200
|
+
process.stdout.write(`\n`);
|
|
201
|
+
printChemistry(prevState, currState.current, locale);
|
|
202
|
+
// Emergent mood
|
|
203
|
+
const mood = describeMood(currState.current, displayLocale);
|
|
204
|
+
const emotions = detectEmotions(currState.current);
|
|
205
|
+
process.stdout.write(`\n ${c(C.dim, "mood:")} ${c(C.bold, mood)}\n`);
|
|
206
|
+
// Policy context (if non-empty)
|
|
207
|
+
if (result.policyContext) {
|
|
208
|
+
process.stdout.write(` ${c(C.dim, "policy:")} ${c(C.yellow, result.policyContext.slice(0, 80))}\n`);
|
|
209
|
+
}
|
|
210
|
+
// Special alerts from policy context
|
|
211
|
+
if (result.policyContext) {
|
|
212
|
+
if (result.policyContext.includes("谄媚") || result.policyContext.includes("sycophancy")) {
|
|
213
|
+
printAlert("ANTI-SYCOPHANCY triggered");
|
|
214
|
+
}
|
|
215
|
+
if (result.policyContext.includes("防御") || result.policyContext.includes("defensive")) {
|
|
216
|
+
printInfo("DEFENSIVE STRATEGY active");
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// Low compliance = pushing back
|
|
220
|
+
if (result.policyModifiers && result.policyModifiers.compliance < 0.4) {
|
|
221
|
+
printInfo(`COMPLIANCE: ${result.policyModifiers.compliance.toFixed(2)} (pushing back)`);
|
|
222
|
+
}
|
|
223
|
+
// Trait drift check
|
|
224
|
+
if (currState.traitDrift) {
|
|
225
|
+
const drifts = Object.entries(currState.traitDrift).filter(([, v]) => typeof v === "number" && Math.abs(v) >= 0.5);
|
|
226
|
+
if (drifts.length > 0) {
|
|
227
|
+
const driftStr = drifts.map(([k, v]) => `${k} ${v > 0 ? "+" : ""}${v.toFixed(1)}`).join(", ");
|
|
228
|
+
printAlert(`TRAIT DRIFT: ${driftStr} (irreversible)`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Drive warnings
|
|
232
|
+
if (currState.drives) {
|
|
233
|
+
const low = Object.entries(currState.drives).filter(([, v]) => v < 40);
|
|
234
|
+
if (low.length > 0) {
|
|
235
|
+
const driveStr = low.map(([k, v]) => `${k}=${Math.round(v)}`).join(", ");
|
|
236
|
+
process.stdout.write(` ${c(C.red, `⚠ low drives: ${driveStr}`)}\n`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
await sleep(pause);
|
|
240
|
+
}
|
|
241
|
+
// ── Final summary ──
|
|
242
|
+
process.stdout.write("\n");
|
|
243
|
+
printLine("═");
|
|
244
|
+
process.stdout.write(c(C.bold, " Final State\n"));
|
|
245
|
+
printLine("═");
|
|
246
|
+
const finalState = engine.getState();
|
|
247
|
+
process.stdout.write("\n");
|
|
248
|
+
printChemistry(finalState.current, finalState.current, locale);
|
|
249
|
+
const finalMood = describeMood(finalState.current, displayLocale);
|
|
250
|
+
process.stdout.write(`\n ${c(C.dim, "mood:")} ${c(C.bold, finalMood)}\n`);
|
|
251
|
+
process.stdout.write(` ${c(C.dim, "summary:")} ${engine.getStatusSummary()}\n`);
|
|
252
|
+
// Drives
|
|
253
|
+
if (finalState.drives) {
|
|
254
|
+
process.stdout.write(`\n ${c(C.dim, "drives:")}\n`);
|
|
255
|
+
for (const [k, v] of Object.entries(finalState.drives)) {
|
|
256
|
+
const val = Math.round(v);
|
|
257
|
+
process.stdout.write(` ${k.padEnd(12)} ${bar(val, 15)} ${val}\n`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
process.stdout.write("\n");
|
|
261
|
+
printLine("─");
|
|
262
|
+
process.stdout.write(c(C.dim, " Try it yourself: ") +
|
|
263
|
+
c(C.cyan, "npx psyche-mcp") +
|
|
264
|
+
c(C.dim, " (configure in Claude Desktop / Cursor / Claude Code)\n"));
|
|
265
|
+
process.stdout.write(c(C.dim, " npm: ") +
|
|
266
|
+
c(C.cyan, "https://www.npmjs.com/package/psyche-ai") + "\n");
|
|
267
|
+
printLine("─");
|
|
268
|
+
process.stdout.write("\n");
|
|
269
|
+
}
|
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/Shangri-la-0428/psyche-ai",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "9.2.
|
|
9
|
+
"version": "9.2.3",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "psyche-ai",
|
|
14
|
-
"version": "9.2.
|
|
14
|
+
"version": "9.2.3",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|