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 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
@@ -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.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
@@ -0,0 +1,5 @@
1
+ export declare function runDemo(opts?: {
2
+ locale?: string;
3
+ mbti?: string;
4
+ fast?: boolean;
5
+ }): Promise<void>;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "psyche-ai",
3
- "version": "9.2.2",
3
+ "version": "9.2.3",
4
4
  "description": "Artificial Psyche — universal emotional intelligence plugin for any AI agent",
5
5
  "mcpName": "io.github.Shangri-la-0428/psyche-ai",
6
6
  "type": "module",
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.2",
9
+ "version": "9.2.3",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "psyche-ai",
14
- "version": "9.2.2",
14
+ "version": "9.2.3",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },