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/dist/cli.js CHANGED
@@ -3,21 +3,24 @@
3
3
  // psyche — Artificial Psyche CLI (v0.2)
4
4
  //
5
5
  // Usage:
6
- // psyche init <dir> [--mbti TYPE] [--name NAME] [--lang LOCALE]
6
+ // psyche init <dir> [--mbti TYPE] [--name NAME] [--lang LOCALE] [--mode MODE] [--traits "O:80,C:40,E:90,A:60,N:30"] [--no-persist]
7
7
  // psyche status <dir> [--json] [--user ID]
8
8
  // psyche inject <dir> [--protocol] [--json] [--lang LOCALE] [--user ID]
9
9
  // psyche decay <dir>
10
10
  // psyche update <dir> '{"DA":80,"CORT":45}' [--user ID]
11
- // psyche reset <dir>
11
+ // psyche mode <dir> <natural|work|companion>
12
+ // psyche intensity Show info about personality intensity config
13
+ // psyche reset <dir> [--full]
12
14
  // psyche profiles [--json] [--mbti TYPE]
13
15
  // ============================================================
14
16
  import { resolve } from "node:path";
15
17
  import { parseArgs } from "node:util";
16
18
  import { loadState, saveState, decayAndSave, initializeState, mergeUpdates, generatePsycheMd, getRelationship, } from "./psyche-file.js";
17
- import { describeEmotionalState, getExpressionHint } from "./chemistry.js";
18
- import { getBaseline, getTemperament, getSensitivity, getDefaultSelfModel } from "./profiles.js";
19
+ import { describeEmotionalState, getExpressionHint, detectEmotions } from "./chemistry.js";
20
+ import { getBaseline, getTemperament, getSensitivity, getDefaultSelfModel, traitsToBaseline } from "./profiles.js";
19
21
  import { buildDynamicContext, buildProtocolContext } from "./prompt.js";
20
- import { CHEMICAL_KEYS, CHEMICAL_NAMES_ZH } from "./types.js";
22
+ import { t } from "./i18n.js";
23
+ import { CHEMICAL_KEYS, CHEMICAL_NAMES_ZH, DRIVE_KEYS, DRIVE_NAMES_ZH } from "./types.js";
21
24
  import { isMBTIType, isChemicalKey, isLocale } from "./guards.js";
22
25
  // ── Logger ───────────────────────────────────────────────────
23
26
  const cliLogger = {
@@ -49,12 +52,32 @@ function printChemistry(state) {
49
52
  console.log(` ${label} ${nameZh} ${bar(val)} ${String(val).padStart(3)} (base:${base} ${a})`);
50
53
  }
51
54
  }
55
+ function printDrives(state) {
56
+ const icons = { survival: "🛡️", safety: "🏠", connection: "🤝", esteem: "⭐", curiosity: "🔍" };
57
+ for (const key of DRIVE_KEYS) {
58
+ const val = Math.round(state.drives[key]);
59
+ const icon = icons[key] ?? "·";
60
+ const status = val >= 60 ? "🟢" : val >= 40 ? "🟡" : "🔴";
61
+ const nameZh = DRIVE_NAMES_ZH[key].padEnd(6);
62
+ console.log(` ${icon} ${nameZh} ${bar(val, 20)} ${String(val).padStart(3)} ${status}`);
63
+ }
64
+ }
65
+ function printEmotions(state) {
66
+ const emotions = detectEmotions(state.current);
67
+ if (emotions.length > 0) {
68
+ const names = emotions.map((e) => e.nameZh).join("、");
69
+ console.log(` 涌现情绪: ${names}`);
70
+ }
71
+ else {
72
+ console.log(` 涌现情绪: 平静`);
73
+ }
74
+ }
52
75
  function die(msg) {
53
76
  console.error(`error: ${msg}`);
54
77
  process.exit(1);
55
78
  }
56
79
  // ── Commands ─────────────────────────────────────────────────
57
- async function cmdInit(dir, mbti, name, lang) {
80
+ async function cmdInit(dir, mbti, name, lang, mode, traits, noPersist) {
58
81
  const absDir = resolve(dir);
59
82
  const opts = {};
60
83
  if (mbti) {
@@ -70,7 +93,46 @@ async function cmdInit(dir, mbti, name, lang) {
70
93
  die(`invalid locale: ${lang}. Valid: zh, en`);
71
94
  opts.locale = lang;
72
95
  }
96
+ if (mode) {
97
+ const validModes = ["natural", "work", "companion"];
98
+ if (!validModes.includes(mode))
99
+ die(`invalid mode: ${mode}. Valid: ${validModes.join(", ")}`);
100
+ }
101
+ // Parse traits string like "O:80,C:40,E:90,A:60,N:30"
102
+ let parsedTraits;
103
+ if (traits) {
104
+ const traitMap = {
105
+ O: "openness", C: "conscientiousness", E: "extraversion", A: "agreeableness", N: "neuroticism",
106
+ };
107
+ const parsed = {};
108
+ for (const pair of traits.split(",")) {
109
+ const [key, val] = pair.trim().split(":");
110
+ const traitKey = traitMap[key?.toUpperCase()];
111
+ if (!traitKey)
112
+ die(`invalid trait key: ${key}. Valid: O, C, E, A, N`);
113
+ const num = parseInt(val, 10);
114
+ if (isNaN(num) || num < 0 || num > 100)
115
+ die(`trait value must be 0-100: ${val}`);
116
+ parsed[traitKey] = num;
117
+ }
118
+ if (Object.keys(parsed).length !== 5)
119
+ die("all 5 traits required: O, C, E, A, N");
120
+ parsedTraits = parsed;
121
+ }
73
122
  const state = await initializeState(absDir, opts, cliLogger);
123
+ // Apply parsed traits to override baseline
124
+ if (parsedTraits) {
125
+ const { baseline } = traitsToBaseline(parsedTraits);
126
+ state.baseline = baseline;
127
+ state.current = { ...baseline };
128
+ await saveState(absDir, state);
129
+ await generatePsycheMd(absDir, state);
130
+ }
131
+ // Apply mode after initialization
132
+ if (mode) {
133
+ state.meta.mode = mode;
134
+ await saveState(absDir, state);
135
+ }
74
136
  console.log(`\nPsyche initialized for ${state.meta.agentName} (${state.mbti})\n`);
75
137
  printChemistry(state);
76
138
  console.log(`\nFiles created:`);
@@ -98,6 +160,10 @@ async function cmdStatus(dir, json, userId) {
98
160
  const elapsed = ((Date.now() - new Date(state.updatedAt).getTime()) / 60000).toFixed(1);
99
161
  console.log(`\n${state.meta.agentName} (${state.mbti}) — ${emotion}\n`);
100
162
  printChemistry(state);
163
+ console.log();
164
+ printDrives(state);
165
+ printEmotions(state);
166
+ console.log(` Mode: ${state.meta.mode ?? "natural"} — ${t(`mode.${state.meta.mode ?? "natural"}`, locale)}`);
101
167
  console.log(`\n Expression: ${hint}`);
102
168
  console.log(` Relationship (${userId ?? "_default"}): trust ${relationship.trust}, intimacy ${relationship.intimacy} (${relationship.phase})`);
103
169
  console.log(` Interactions: ${state.meta.totalInteractions}`);
@@ -176,17 +242,22 @@ async function cmdUpdate(dir, updateJson, userId) {
176
242
  printChemistry(merged);
177
243
  console.log();
178
244
  }
179
- async function cmdReset(dir) {
245
+ async function cmdReset(dir, full) {
180
246
  const absDir = resolve(dir);
181
247
  const state = await loadState(absDir, cliLogger);
182
248
  state.current = { ...state.baseline };
249
+ state.drives = { survival: 80, safety: 70, connection: 60, esteem: 60, curiosity: 70 };
183
250
  state.updatedAt = new Date().toISOString();
184
251
  state.empathyLog = null;
185
252
  state.agreementStreak = 0;
186
253
  state.lastDisagreement = null;
254
+ state.emotionalHistory = [];
255
+ if (full) {
256
+ state.relationships = { _default: { trust: 50, intimacy: 30, phase: "acquaintance" } };
257
+ }
187
258
  await saveState(absDir, state);
188
259
  await generatePsycheMd(absDir, state);
189
- console.log(`\n${state.meta.agentName} reset to baseline (${state.mbti})\n`);
260
+ console.log(`\n${state.meta.agentName} reset to baseline (${state.mbti})${full ? " [full reset including relationships]" : ""}\n`);
190
261
  printChemistry(state);
191
262
  console.log();
192
263
  }
@@ -256,6 +327,32 @@ function cmdProfiles(json, mbti) {
256
327
  console.log();
257
328
  }
258
329
  }
330
+ async function cmdMode(dir, newMode) {
331
+ const absDir = resolve(dir);
332
+ const validModes = ["natural", "work", "companion"];
333
+ if (!validModes.includes(newMode))
334
+ die(`invalid mode: ${newMode}. Valid: ${validModes.join(", ")}`);
335
+ const state = await loadState(absDir, cliLogger);
336
+ state.meta.mode = newMode;
337
+ await saveState(absDir, state);
338
+ console.log(`\n${state.meta.agentName} mode set to: ${newMode}\n`);
339
+ }
340
+ function cmdIntensity() {
341
+ console.log(`
342
+ Personality intensity controls how strongly Psyche affects AI behavior.
343
+
344
+ 0.0 = Traditional warm AI (no emotional variation)
345
+ 0.5 = Subtle emotional responses
346
+ 0.7 = Default
347
+ 1.0 = Full Psyche experience
348
+
349
+ Set via PsycheEngine config:
350
+ new PsycheEngine({ personalityIntensity: 0.7 }, storage)
351
+
352
+ Or in OpenClaw plugin config:
353
+ { "personalityIntensity": 0.7 }
354
+ `);
355
+ }
259
356
  // ── Usage ────────────────────────────────────────────────────
260
357
  function usage() {
261
358
  console.log(`
@@ -263,12 +360,14 @@ psyche — Artificial Psyche CLI (v0.2)
263
360
  Plug-and-play emotional intelligence for any AI agent.
264
361
 
265
362
  Usage:
266
- psyche init <dir> [--mbti TYPE] [--name NAME] [--lang LOCALE]
363
+ psyche init <dir> [--mbti TYPE] [--name NAME] [--lang LOCALE] [--mode MODE] [--traits "O:80,C:40,..."] [--no-persist]
267
364
  psyche status <dir> [--json] [--user ID]
268
365
  psyche inject <dir> [--protocol] [--json] [--lang LOCALE] [--user ID]
269
366
  psyche decay <dir>
270
367
  psyche update <dir> '{"DA":80,"CORT":45}' [--user ID]
271
- psyche reset <dir>
368
+ psyche mode <dir> <natural|work|companion>
369
+ psyche intensity Show info about personality intensity config
370
+ psyche reset <dir> [--full]
272
371
  psyche profiles [--mbti TYPE] [--json]
273
372
 
274
373
  Options:
@@ -318,12 +417,15 @@ async function main() {
318
417
  mbti: { type: "string" },
319
418
  name: { type: "string" },
320
419
  lang: { type: "string" },
420
+ mode: { type: "string" },
421
+ traits: { type: "string" },
422
+ "no-persist": { type: "boolean", default: false },
321
423
  },
322
424
  allowPositionals: true,
323
425
  });
324
426
  if (positionals.length === 0)
325
427
  die("missing <dir> argument");
326
- await cmdInit(positionals[0], values.mbti, values.name, values.lang);
428
+ await cmdInit(positionals[0], values.mbti, values.name, values.lang, values.mode, values.traits, values["no-persist"]);
327
429
  break;
328
430
  }
329
431
  case "status": {
@@ -377,9 +479,16 @@ async function main() {
377
479
  break;
378
480
  }
379
481
  case "reset": {
380
- if (rest.length === 0)
482
+ const { values: resetVals, positionals: resetPos } = parseArgs({
483
+ args: rest,
484
+ options: {
485
+ full: { type: "boolean", default: false },
486
+ },
487
+ allowPositionals: true,
488
+ });
489
+ if (resetPos.length === 0)
381
490
  die("missing <dir> argument");
382
- await cmdReset(rest[0]);
491
+ await cmdReset(resetPos[0], resetVals.full ?? false);
383
492
  break;
384
493
  }
385
494
  case "profiles": {
@@ -394,6 +503,16 @@ async function main() {
394
503
  cmdProfiles(values.json ?? false, values.mbti);
395
504
  break;
396
505
  }
506
+ case "mode": {
507
+ if (rest.length < 2)
508
+ die("usage: psyche mode <dir> <natural|work|companion>");
509
+ await cmdMode(rest[0], rest[1]);
510
+ break;
511
+ }
512
+ case "intensity": {
513
+ cmdIntensity();
514
+ break;
515
+ }
397
516
  default:
398
517
  die(`unknown command: ${command}. Run 'psyche help' for usage.`);
399
518
  }
package/dist/core.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { PsycheState, StimulusType, Locale, MBTIType, OutcomeScore } from "./types.js";
1
+ import type { PsycheState, StimulusType, Locale, MBTIType, OutcomeScore, PsycheMode, PersonalityTraits } from "./types.js";
2
2
  import type { StorageAdapter } from "./storage.js";
3
3
  export interface PsycheEngineConfig {
4
4
  mbti?: MBTIType;
@@ -9,6 +9,14 @@ export interface PsycheEngineConfig {
9
9
  maxChemicalDelta?: number;
10
10
  /** Compact mode: algorithms handle chemistry, LLM only sees behavioral output. Default: true */
11
11
  compactMode?: boolean;
12
+ /** Operating mode: "natural" (default), "work" (minimal emotions), "companion" (full emotions) */
13
+ mode?: PsycheMode;
14
+ /** Personality intensity: 0.0 (traditional warm AI) to 1.0 (full Psyche). Default: 0.7 */
15
+ personalityIntensity?: number;
16
+ /** Whether to persist state to disk. Default: true. When false, uses in-memory storage. */
17
+ persist?: boolean;
18
+ /** Big Five traits. If provided, overrides MBTI for baseline calculation. */
19
+ traits?: PersonalityTraits;
12
20
  }
13
21
  export interface ProcessInputResult {
14
22
  /** Cacheable protocol prompt (stable across turns) */
@@ -32,7 +40,10 @@ export interface ProcessOutcomeResult {
32
40
  }
33
41
  export declare class PsycheEngine {
34
42
  private state;
35
- private readonly storage;
43
+ private storage;
44
+ /** Whether the algorithm applied a stimulus in the last processInput call */
45
+ private _lastAlgorithmApplied;
46
+ private readonly traits;
36
47
  private readonly cfg;
37
48
  private readonly protocolCache;
38
49
  /** Pending prediction from last processInput for auto-learning */
@@ -77,6 +88,24 @@ export declare class PsycheEngine {
77
88
  * Get the cacheable protocol prompt for a locale.
78
89
  */
79
90
  getProtocol(locale?: Locale): string;
91
+ /**
92
+ * End the current session: compress emotionalHistory into a rich summary
93
+ * stored in relationship.memory[], then clear the history.
94
+ * No-op if history has fewer than 2 entries.
95
+ */
96
+ endSession(opts?: {
97
+ userId?: string;
98
+ }): Promise<void>;
80
99
  private ensureInitialized;
81
100
  private createDefaultState;
101
+ /**
102
+ * Reset state to baseline. Optionally preserves relationships.
103
+ */
104
+ resetState(opts?: {
105
+ preserveRelationships?: boolean;
106
+ }): Promise<void>;
107
+ /**
108
+ * Get a single-line status summary with emoji.
109
+ */
110
+ getStatusSummary(): string;
82
111
  }