psyche-ai 11.4.0 → 11.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  # Psyche — 赋予 AI 自我的主观性内核
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/psyche-ai)](https://www.npmjs.com/package/psyche-ai)
4
- [![tests](https://img.shields.io/badge/tests-1427%20passing-brightgreen)]()
4
+ [![tests](https://img.shields.io/badge/tests-1433%20passing-brightgreen)]()
5
5
  [![deps](https://img.shields.io/badge/dependencies-0-blue)]()
6
6
  [![license](https://img.shields.io/badge/license-MIT-yellow)](LICENSE)
7
7
 
@@ -578,7 +578,7 @@ Psyche 核心引擎永久开源(MIT)。
578
578
  ```bash
579
579
  npm install
580
580
  npm run build
581
- npm test # 1427 tests
581
+ npm test # 1433 tests
582
582
  npm run typecheck # strict mode
583
583
  ```
584
584
 
@@ -39,7 +39,8 @@ const NAME = process.env.PSYCHE_NAME ?? "Assistant";
39
39
  const MODE = (process.env.PSYCHE_MODE ?? "natural");
40
40
  const LOCALE = (process.env.PSYCHE_LOCALE ?? "en");
41
41
  const PERSIST = process.env.PSYCHE_PERSIST !== "false";
42
- const WORKSPACE = process.env.PSYCHE_WORKSPACE ?? process.cwd();
42
+ const SIGIL_ID = process.env.PSYCHE_SIGIL_ID ?? undefined;
43
+ const BASE_WORKSPACE = process.env.PSYCHE_WORKSPACE ?? process.cwd();
43
44
  const INTENSITY = process.env.PSYCHE_INTENSITY
44
45
  ? Number(process.env.PSYCHE_INTENSITY)
45
46
  : 0.7;
@@ -70,6 +71,10 @@ function parseCLIArgs() {
70
71
  overrides.personalityIntensity = Number(next);
71
72
  i++;
72
73
  }
74
+ else if (arg === "--sigil-id" && next) {
75
+ overrides.sigilId = next;
76
+ i++;
77
+ }
73
78
  else if (arg === "--no-persist") {
74
79
  overrides.persist = false;
75
80
  }
@@ -82,19 +87,23 @@ async function getEngine() {
82
87
  if (engine)
83
88
  return engine;
84
89
  const cliArgs = parseCLIArgs();
90
+ const sigilId = cliArgs.sigilId ?? SIGIL_ID;
85
91
  const cfg = {
86
92
  mbti: cliArgs.mbti ?? MBTI,
87
93
  name: cliArgs.name ?? NAME,
88
94
  mode: cliArgs.mode ?? MODE,
89
95
  locale: cliArgs.locale ?? LOCALE,
96
+ sigilId,
90
97
  personalityIntensity: cliArgs.personalityIntensity ?? INTENSITY,
91
98
  persist: cliArgs.persist ?? PERSIST,
92
99
  compactMode: true,
93
100
  diagnostics: true,
94
101
  };
95
102
  const persist = cfg.persist !== false;
103
+ // Per-Sigil workspace isolation: each Sigil gets its own state directory
104
+ const workspace = sigilId ? `${BASE_WORKSPACE}/${sigilId}` : BASE_WORKSPACE;
96
105
  const storage = persist
97
- ? new FileStorageAdapter(WORKSPACE)
106
+ ? new FileStorageAdapter(workspace)
98
107
  : new MemoryStorageAdapter();
99
108
  engine = new PsycheEngine(cfg, storage);
100
109
  await engine.initialize();
@@ -103,7 +112,7 @@ async function getEngine() {
103
112
  // ── MCP Server ─────────────────────────────────────────────
104
113
  const server = new McpServer({
105
114
  name: "psyche",
106
- version: "9.2.3",
115
+ version: "11.4.0",
107
116
  }, {
108
117
  capabilities: {
109
118
  resources: {},
package/dist/core.d.ts CHANGED
@@ -6,6 +6,8 @@ export interface PsycheEngineConfig {
6
6
  mbti?: MBTIType;
7
7
  name?: string;
8
8
  locale?: Locale;
9
+ /** Sigil ID — which Loop this Psyche instance serves. When set, state persists per-Sigil. */
10
+ sigilId?: string;
9
11
  stripUpdateTags?: boolean;
10
12
  emotionalContagionRate?: number;
11
13
  maxDimensionDelta?: number;
package/dist/core.js CHANGED
@@ -146,6 +146,7 @@ export class PsycheEngine {
146
146
  mbti: config.mbti ?? "INFJ",
147
147
  name: config.name ?? "agent",
148
148
  locale: config.locale ?? "zh",
149
+ sigilId: config.sigilId,
149
150
  stripUpdateTags: config.stripUpdateTags ?? true,
150
151
  emotionalContagionRate: config.emotionalContagionRate ?? 0.2,
151
152
  maxDimensionDelta: config.maxDimensionDelta ?? 25,
@@ -221,6 +222,10 @@ export class PsycheEngine {
221
222
  if (!loaded.lastWritebackFeedback) {
222
223
  loaded.lastWritebackFeedback = [];
223
224
  }
225
+ // Update sigilId if config provides one (Sigil may be assigned after first run)
226
+ if (this.cfg.sigilId && loaded.meta.sigilId !== this.cfg.sigilId) {
227
+ loaded.meta = { ...loaded.meta, sigilId: this.cfg.sigilId };
228
+ }
224
229
  this.state = loaded;
225
230
  }
226
231
  else {
@@ -825,6 +830,7 @@ export class PsycheEngine {
825
830
  totalInteractions: 0,
826
831
  locale,
827
832
  mode: this.cfg.mode,
833
+ ...(this.cfg.sigilId ? { sigilId: this.cfg.sigilId } : {}),
828
834
  },
829
835
  };
830
836
  }
@@ -892,6 +898,7 @@ export class PsycheEngine {
892
898
  const driveWarning = hungryDrives.length > 0
893
899
  ? ` | \u26A0\uFE0F${hungryDrives.join(",")}`
894
900
  : "";
895
- return `${emoji} ${emotion} | flow:${Math.round(flow)} order:${Math.round(order)}${driveWarning}`;
901
+ const sigilTag = state.meta.sigilId ? ` | sigil:${state.meta.sigilId}` : "";
902
+ return `${emoji} ${emotion} | flow:${Math.round(flow)} order:${Math.round(order)}${driveWarning}${sigilTag}`;
896
903
  }
897
904
  }
@@ -45,6 +45,29 @@ function updateExportState(state, keys, now) {
45
45
  throngletsExportState: nextState,
46
46
  };
47
47
  }
48
+ function quantize(v, step = 10) {
49
+ return Math.round(v / step) * step;
50
+ }
51
+ function selfStateSummary(o, f, b, r) {
52
+ const parts = [];
53
+ if (o > 65)
54
+ parts.push("structured");
55
+ else if (o < 35)
56
+ parts.push("chaotic");
57
+ if (f > 65)
58
+ parts.push("flowing");
59
+ else if (f < 35)
60
+ parts.push("stuck");
61
+ if (b > 65)
62
+ parts.push("open");
63
+ else if (b < 35)
64
+ parts.push("guarded");
65
+ if (r > 65)
66
+ parts.push("attuned");
67
+ else if (r < 35)
68
+ parts.push("dissonant");
69
+ return parts.length > 0 ? parts.join(", ") : "neutral";
70
+ }
48
71
  function sanitizeThrongletsExport(event) {
49
72
  switch (event.kind) {
50
73
  case "relation-milestone": {
@@ -108,6 +131,23 @@ function sanitizeThrongletsExport(event) {
108
131
  };
109
132
  return sanitized;
110
133
  }
134
+ case "self-state": {
135
+ const sanitized = {
136
+ kind: "self-state",
137
+ subject: "session",
138
+ primitive: "signal",
139
+ userKey: event.userKey,
140
+ strength: event.strength,
141
+ ttlTurns: event.ttlTurns,
142
+ key: event.key,
143
+ order: event.order,
144
+ flow: event.flow,
145
+ boundary: event.boundary,
146
+ resonance: event.resonance,
147
+ summary: event.summary,
148
+ };
149
+ return sanitized;
150
+ }
111
151
  }
112
152
  }
113
153
  export function deriveThrongletsExports(state, opts) {
@@ -166,6 +206,20 @@ export function deriveThrongletsExports(state, opts) {
166
206
  silentCarry: field.silentCarry,
167
207
  });
168
208
  }
209
+ // Self-state — sparse, only emits when dimensions shift by ≥10
210
+ const { order, flow, boundary, resonance } = state.current;
211
+ const selfKey = `self-state:O${quantize(order)}:F${quantize(flow)}:B${quantize(boundary)}:R${quantize(resonance)}`;
212
+ candidates.push({
213
+ kind: "self-state",
214
+ subject: "session",
215
+ primitive: "signal",
216
+ userKey,
217
+ strength: 0.5,
218
+ ttlTurns: 12,
219
+ key: selfKey,
220
+ order, flow, boundary, resonance,
221
+ summary: selfStateSummary(order, flow, boundary, resonance),
222
+ });
169
223
  for (const feedback of writebackFeedback) {
170
224
  if (feedback.effect === "holding")
171
225
  continue;
@@ -3,6 +3,7 @@ const TAXONOMY_BY_EVENT = {
3
3
  "open-loop-anchor": "coordination",
4
4
  "continuity-anchor": "continuity",
5
5
  "writeback-calibration": "calibration",
6
+ "self-state": "state",
6
7
  };
7
8
  function summarizeLoopTypes(loopTypes) {
8
9
  return loopTypes.join(", ");
@@ -19,6 +20,8 @@ function summarizeThrongletsExport(event) {
19
20
  const calibration = event;
20
21
  return `writeback calibration ${calibration.signal} ${calibration.effect} on ${calibration.metric}`;
21
22
  }
23
+ case "self-state":
24
+ return event.summary;
22
25
  }
23
26
  }
24
27
  export function taxonomyForThrongletsExport(event) {
package/dist/types.d.ts CHANGED
@@ -374,6 +374,8 @@ export interface PsycheState {
374
374
  totalInteractions: number;
375
375
  locale: Locale;
376
376
  mode?: PsycheMode;
377
+ /** Sigil ID — which Loop this Psyche instance serves */
378
+ sigilId?: string;
377
379
  };
378
380
  }
379
381
  /** Default relationship for new users */
@@ -560,7 +562,7 @@ export interface SessionBridgeState {
560
562
  export type ThrongletsExportSubject = "delegate" | "session";
561
563
  export type ThrongletsExportPrimitive = "signal" | "trace";
562
564
  export interface ThrongletsExportBase {
563
- kind: "relation-milestone" | "open-loop-anchor" | "writeback-calibration" | "continuity-anchor";
565
+ kind: "relation-milestone" | "open-loop-anchor" | "writeback-calibration" | "continuity-anchor" | "self-state";
564
566
  subject: ThrongletsExportSubject;
565
567
  primitive: ThrongletsExportPrimitive;
566
568
  userKey: string;
@@ -601,7 +603,17 @@ export interface ContinuityAnchorExport extends ThrongletsExportBase {
601
603
  activeLoopTypes: OpenLoopType[];
602
604
  continuityFloor: number;
603
605
  }
604
- export type ThrongletsExport = RelationMilestoneExport | OpenLoopAnchorExport | WritebackCalibrationExport | ContinuityAnchorExport;
606
+ export interface SelfStateExport extends ThrongletsExportBase {
607
+ kind: "self-state";
608
+ subject: "session";
609
+ primitive: "signal";
610
+ order: number;
611
+ flow: number;
612
+ boundary: number;
613
+ resonance: number;
614
+ summary: string;
615
+ }
616
+ export type ThrongletsExport = RelationMilestoneExport | OpenLoopAnchorExport | WritebackCalibrationExport | ContinuityAnchorExport | SelfStateExport;
605
617
  export interface ThrongletsExportState {
606
618
  lastKeys: string[];
607
619
  lastAt: string;
@@ -615,7 +627,7 @@ export interface ExternalContinuityEnvelope<TEvent = ExternalContinuityEvent> {
615
627
  signals: TEvent[];
616
628
  traces: TEvent[];
617
629
  }
618
- export type ThrongletsTraceTaxonomy = "coordination" | "continuity" | "calibration";
630
+ export type ThrongletsTraceTaxonomy = "coordination" | "continuity" | "calibration" | "state";
619
631
  export interface ThrongletsExternalContinuityRecord {
620
632
  provider: "thronglets";
621
633
  mode: "optional";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "psyche-ai",
3
- "version": "11.4.0",
3
+ "version": "11.5.0",
4
4
  "description": "AI-first subjectivity kernel for agents with continuous appraisal, relation dynamics, and adaptive reply loops",
5
5
  "mcpName": "io.github.Shangri-la-0428/psyche-ai",
6
6
  "type": "module",