psyche-ai 9.2.10 → 10.0.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-first 主观性内核
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-1308%20passing-brightgreen)]()
4
+ [![tests](https://img.shields.io/badge/tests-1316%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
 
@@ -63,6 +63,11 @@ Psyche 要解决的不可压缩问题只有一个:
63
63
  - `Oasyce Net = policy, operations, and resource orchestration`
64
64
  - `Oasyce Chain = account truth, authorization truth, commitments, settlement, and public finality`
65
65
 
66
+ 授权真相流也固定成单向:
67
+ `Chain -> Net -> Thronglets -> Psyche`
68
+
69
+ 也就是说,`Psyche` 不判断“谁被授权”,它只读取已经成立的执行边界结果。
70
+
66
71
  正式版本见:
67
72
 
68
73
  - [docs/IDENTITY_MODEL.md](docs/IDENTITY_MODEL.md)
@@ -179,11 +184,17 @@ npx psyche-mcp --demo
179
184
 
180
185
  ## 30 秒安装
181
186
 
187
+ ```bash
188
+ npm install psyche-ai
189
+ ```
190
+
191
+ 或者通过 OpenClaw 插件安装:
192
+
182
193
  ```bash
183
194
  openclaw plugins install psyche-ai
184
195
  ```
185
196
 
186
- 没了。装完自动生效。
197
+ 验证:
187
198
 
188
199
  ```bash
189
200
  openclaw plugins list | grep psyche
@@ -326,11 +337,13 @@ Psyche 现在更准确的目标,不是“模仿某种像人的风格”,而
326
337
 
327
338
  当前主线里,`processInput()` 会直接返回 `replyEnvelope` 作为规范主接口;原有平级字段继续保留为兼容别名,避免打断现有宿主。`policyModifiers` 不再属于规范主接口,只保留为 legacy raw vector。
328
339
 
329
- 在这个规范主接口之外,`processInput()` 现在还会可选返回一个很薄的 `observability` side-channel。它不是新的控制面,也不会反向改写 `replyEnvelope`;它只回答三件事:
340
+ 在这个规范主接口之外,`processInput()` 现在还会可选返回一个很薄的 `observability` side-channel。它不是新的控制面,也不会反向改写 `replyEnvelope`;它只回答五件事:
330
341
 
331
342
  - 这轮当前由哪一个控制平面主导
332
343
  - 当前 turn / writeback / session bridge / persisted relationship 四层是怎么对账的
333
344
  - 这次 `work/private` 选择为什么会落在这个 profile 上
345
+ - 这轮和上一轮、session bridge、writeback、external continuity 之间的因果引用链是什么
346
+ - 当前 low-frequency external continuity 事件如何映射成 `localTraceRefs / signalRefs / traceRefs / summaryCandidateRefs`
334
347
 
335
348
  这样别的 agent 或宿主不用再自己猜“现在到底是谁在主导”和“为什么这次选了这个策略”,但主链路仍然只有 `replyEnvelope` 一个真相源。
336
349
 
@@ -555,7 +568,7 @@ Psyche 核心引擎永久开源(MIT)。
555
568
  ```bash
556
569
  npm install
557
570
  npm run build
558
- npm test # 1308 tests
571
+ npm test # 1316 tests
559
572
  npm run typecheck # strict mode
560
573
  ```
561
574
 
package/dist/cli.js CHANGED
@@ -140,7 +140,7 @@ async function cmdInit(dir, mbti, name, lang, mode, traits, _noPersist) {
140
140
  state.meta.mode = mode;
141
141
  await saveState(absDir, state);
142
142
  }
143
- console.log(`\nPsyche initialized for ${state.meta.agentName} (${state.mbti})\n`);
143
+ console.log(`\nPsyche initialized for ${state.meta.agentName}${state.mbti ? ` (preset: ${state.mbti})` : ""}\n`);
144
144
  printChemistry(state);
145
145
  console.log(`\nFiles created:`);
146
146
  console.log(` ${absDir}/psyche-state.json`);
@@ -165,7 +165,12 @@ async function cmdStatus(dir, json, userId) {
165
165
  const emotion = describeEmotionalState(state.current, locale);
166
166
  const hint = getExpressionHint(state.current, locale);
167
167
  const elapsed = ((Date.now() - new Date(state.updatedAt).getTime()) / 60000).toFixed(1);
168
- console.log(`\n${state.meta.agentName} (${state.mbti}) ${emotion}\n`);
168
+ const baselineSummary = [
169
+ state.baseline.DA < 55 ? "introvert" : "extrovert",
170
+ state.baseline.DA > state.baseline.HT ? "intuitive" : "sensing",
171
+ state.baseline.OT >= 50 ? "feeling" : "thinking",
172
+ ].join("/");
173
+ console.log(`\n${state.meta.agentName} (${baselineSummary}) — ${emotion}\n`);
169
174
  printChemistry(state);
170
175
  console.log();
171
176
  printDrives(state);
@@ -264,7 +269,7 @@ async function cmdReset(dir, full) {
264
269
  }
265
270
  await saveState(absDir, state);
266
271
  await generatePsycheMd(absDir, state);
267
- console.log(`\n${state.meta.agentName} reset to baseline (${state.mbti})${full ? " [full reset including relationships]" : ""}\n`);
272
+ console.log(`\n${state.meta.agentName} reset to baseline${full ? " [full reset including relationships]" : ""}\n`);
268
273
  printChemistry(state);
269
274
  console.log();
270
275
  }
package/dist/core.d.ts CHANGED
@@ -9,7 +9,7 @@ export interface PsycheEngineConfig {
9
9
  stripUpdateTags?: boolean;
10
10
  emotionalContagionRate?: number;
11
11
  maxChemicalDelta?: number;
12
- /** Compact mode: algorithms handle chemistry, LLM only sees behavioral output. Default: true */
12
+ /** @deprecated Compact mode is always on since v10. This option is ignored. */
13
13
  compactMode?: boolean;
14
14
  /** Operating mode: "natural" (default), "work" (minimal emotions), "companion" (full emotions) */
15
15
  mode?: PsycheMode;
package/dist/core.js CHANGED
@@ -15,7 +15,7 @@ import { DEFAULT_RELATIONSHIP, DEFAULT_DRIVES, DEFAULT_LEARNING_STATE, DEFAULT_M
15
15
  import { MemoryStorageAdapter } from "./storage.js";
16
16
  import { applyDecay, applyStimulus, applyContagion, clamp, describeEmotionalState } from "./chemistry.js";
17
17
  import { classifyStimulus, BuiltInClassifier, buildLLMClassifierPrompt, parseLLMClassification } from "./classify.js";
18
- import { buildDynamicContext, buildProtocolContext, buildCompactContext } from "./prompt.js";
18
+ import { buildCompactContext, buildProtocolContext } from "./prompt.js";
19
19
  import { getSensitivity, getBaseline, getDefaultSelfModel, traitsToBaseline } from "./profiles.js";
20
20
  import { isStimulusType } from "./guards.js";
21
21
  import { parsePsycheUpdate, mergeUpdates, updateAgreementStreak, pushSnapshot, compressSession, summarizeTurnSemantic, } from "./psyche-file.js";
@@ -297,7 +297,7 @@ export class PsycheEngine {
297
297
  };
298
298
  }
299
299
  // v9: Energy budget recovery during absence + depletion per turn
300
- const isExtravert = this.cfg.mbti.startsWith("E");
300
+ const isExtravert = state.baseline.DA >= 55;
301
301
  const currentBudgets = state.energyBudgets ?? { ...DEFAULT_ENERGY_BUDGETS };
302
302
  let energyBudgets = minutesElapsed >= 5
303
303
  ? computeEnergyRecovery(currentBudgets, minutesElapsed, isExtravert)
@@ -337,7 +337,7 @@ export class PsycheEngine {
337
337
  const preStimulus = current;
338
338
  // Feed drives from stimulus, then apply stimulus with drive-modified sensitivity
339
339
  drives = feedDrives(drives, primary.type);
340
- const effectiveSensitivity = computeEffectiveSensitivity(getSensitivity(state.mbti), drives, primary.type, state.traitDrift);
340
+ const effectiveSensitivity = computeEffectiveSensitivity((state.sensitivity ?? 1.0), drives, primary.type, state.traitDrift);
341
341
  // v9.2: Confidence modulates intensity — a 0.95 life-or-death dilemma
342
342
  // hits ~1.7x harder than a 0.55 mild disagreement.
343
343
  // Maps [0.5, 1.0] → [0.6, 1.2] via linear interpolation.
@@ -423,7 +423,7 @@ export class PsycheEngine {
423
423
  // ── Generate prediction for next turn's auto-learning ────
424
424
  if (appliedStimulus) {
425
425
  const ctxHash = computeContextHash(state, opts?.userId);
426
- const effectiveSensitivity = computeEffectiveSensitivity(getSensitivity(state.mbti), state.drives, appliedStimulus, state.traitDrift);
426
+ const effectiveSensitivity = computeEffectiveSensitivity((state.sensitivity ?? 1.0), state.drives, appliedStimulus, state.traitDrift);
427
427
  const predicted = predictChemistry(preInteractionState.current, appliedStimulus, state.learning, ctxHash, effectiveSensitivity, this.cfg.maxChemicalDelta);
428
428
  this.pendingPrediction = {
429
429
  predictedChemistry: predicted,
@@ -480,6 +480,7 @@ export class PsycheEngine {
480
480
  subjectivityContext: derivedReplyEnvelope.subjectivityContext,
481
481
  responseContractContext: derivedReplyEnvelope.responseContractContext,
482
482
  policyContext: derivedReplyEnvelope.policyContext || undefined,
483
+ sessionBridge,
483
484
  };
484
485
  const observability = buildTurnObservability(state, {
485
486
  replyEnvelope,
@@ -490,32 +491,13 @@ export class PsycheEngine {
490
491
  sessionBridge,
491
492
  writebackFeedback,
492
493
  relationContext: relationalTurn.relationContext,
493
- externalContinuityExports: throngletsExports.length,
494
+ externalContinuityEvents: throngletsExports,
494
495
  });
495
- if (this.cfg.compactMode) {
496
- const externalContinuity = buildExternalContinuityEnvelope(throngletsExports);
497
- return {
498
- systemContext: "",
499
- dynamicContext: buildCompactContext(state, opts?.userId, promptRenderInputs),
500
- stimulus: appliedStimulus,
501
- stimulusConfidence: this.lastStimulusAssessment?.confidence,
502
- replyEnvelope,
503
- policyModifiers: derivedReplyEnvelope.policyModifiers,
504
- subjectivityKernel: replyEnvelope.subjectivityKernel,
505
- responseContract: replyEnvelope.responseContract,
506
- generationControls: replyEnvelope.generationControls,
507
- sessionBridge,
508
- writebackFeedback,
509
- externalContinuity,
510
- throngletsExports,
511
- observability,
512
- policyContext: derivedReplyEnvelope.policyContext,
513
- };
514
- }
496
+ // v10: compact mode is always on. Legacy buildDynamicContext removed from engine path.
515
497
  const externalContinuity = buildExternalContinuityEnvelope(throngletsExports);
516
498
  return {
517
- systemContext: this.getProtocol(locale),
518
- dynamicContext: buildDynamicContext(state, opts?.userId, promptRenderInputs),
499
+ systemContext: "",
500
+ dynamicContext: buildCompactContext(state, opts?.userId, promptRenderInputs),
519
501
  stimulus: appliedStimulus,
520
502
  stimulusConfidence: this.lastStimulusAssessment?.confidence,
521
503
  replyEnvelope,
@@ -559,7 +541,7 @@ export class PsycheEngine {
559
541
  const selfFeedbackRate = 0.3;
560
542
  state = {
561
543
  ...state,
562
- current: applyContagion(state.current, selfPrimary.type, selfFeedbackRate, getSensitivity(state.mbti)),
544
+ current: applyContagion(state.current, selfPrimary.type, selfFeedbackRate, (state.sensitivity ?? 1.0)),
563
545
  };
564
546
  stateChanged = true;
565
547
  // v9.2 P4: Autonomic recovery — expressing vulnerable/comforting emotions
@@ -602,7 +584,7 @@ export class PsycheEngine {
602
584
  ...state,
603
585
  drives: feedDrives(state.drives, parseResult.llmStimulus),
604
586
  };
605
- const effectiveSensitivity = computeEffectiveSensitivity(getSensitivity(state.mbti), state.drives, parseResult.llmStimulus, state.traitDrift);
587
+ const effectiveSensitivity = computeEffectiveSensitivity((state.sensitivity ?? 1.0), state.drives, parseResult.llmStimulus, state.traitDrift);
606
588
  state = {
607
589
  ...state,
608
590
  current: applyStimulus(state.current, parseResult.llmStimulus, effectiveSensitivity * (overrideAllowed && this._lastAlgorithmApplied ? 0.8 : 1), this.cfg.maxChemicalDelta, NOOP_LOGGER),
@@ -786,14 +768,15 @@ export class PsycheEngine {
786
768
  }
787
769
  createDefaultState() {
788
770
  const { mbti, name, locale } = this.cfg;
789
- // Use Big Five traits if provided, otherwise use MBTI baseline
771
+ // Use Big Five traits if provided, otherwise use preset baseline
790
772
  const baseline = this.traits ? traitsToBaseline(this.traits).baseline : getBaseline(mbti);
773
+ const sensitivity = this.traits ? 1.0 : getSensitivity(mbti);
791
774
  const selfModel = getDefaultSelfModel(mbti);
792
775
  const now = new Date().toISOString();
793
776
  return {
794
- version: 9,
795
- mbti,
777
+ version: 10,
796
778
  baseline,
779
+ sensitivity,
797
780
  current: { ...baseline },
798
781
  drives: { ...DEFAULT_DRIVES },
799
782
  updatedAt: now,
@@ -838,7 +821,7 @@ export class PsycheEngine {
838
821
  */
839
822
  async resetState(opts) {
840
823
  let state = this.ensureInitialized();
841
- const baseline = this.traits ? traitsToBaseline(this.traits).baseline : getBaseline(state.mbti);
824
+ const baseline = this.traits ? traitsToBaseline(this.traits).baseline : { ...state.baseline };
842
825
  state = {
843
826
  ...state,
844
827
  current: { ...baseline },
@@ -1,8 +1,15 @@
1
1
  import type { AppraisalAxes, PsycheState, ChemicalState, StimulusType, InnateDrives } from "./types.js";
2
+ /**
3
+ * The four diagnostic layers, matching the frozen stack.
4
+ * When something breaks, the first question is: which layer?
5
+ */
6
+ export type DiagnosticLayer = "subjective-continuity" | "delegate-continuity" | "policy-orchestration" | "public-truth";
2
7
  export type Severity = "critical" | "warning" | "info";
3
8
  export interface DiagnosticIssue {
4
9
  id: string;
5
10
  severity: Severity;
11
+ /** Which stack layer this issue belongs to */
12
+ layer: DiagnosticLayer;
6
13
  message: string;
7
14
  detail?: string;
8
15
  /** Dev-facing: what to fix in our code or config */
@@ -40,8 +47,12 @@ export interface DiagnosticReport {
40
47
  version: string;
41
48
  timestamp: string;
42
49
  agent: string;
43
- mbti: string;
50
+ mbti?: string;
44
51
  issues: DiagnosticIssue[];
52
+ /** Issues grouped by stack layer — same issues as `issues`, partitioned */
53
+ layeredIssues: Record<DiagnosticLayer, DiagnosticIssue[]>;
54
+ /** Per-layer health summary — the first thing to read when something is wrong */
55
+ layerHealth: LayerHealthSummary;
45
56
  metrics: SessionMetrics;
46
57
  stateSnapshot: {
47
58
  chemistry: ChemicalState;
@@ -54,7 +65,47 @@ export interface DiagnosticReport {
54
65
  stateVersion: number;
55
66
  };
56
67
  }
68
+ export type LayerStatus = "healthy" | "degraded" | "failing";
69
+ export interface LayerHealthDetail {
70
+ status: LayerStatus;
71
+ /** Worst severity found in this layer (null = no issues) */
72
+ worstSeverity: Severity | null;
73
+ issueCount: number;
74
+ /** One-line human-readable summary */
75
+ summary: string;
76
+ }
77
+ export interface LayerHealthSummary {
78
+ "subjective-continuity": LayerHealthDetail & {
79
+ /** Chemistry deviation from baseline (0 = identical) */
80
+ chemistryDeviation: number;
81
+ /** Session bridge present and carrying identity state */
82
+ sessionBridgeActive: boolean;
83
+ /** Trait drift trajectory established */
84
+ traitDriftEstablished: boolean;
85
+ /** Number of active dyadic relations */
86
+ activeDyadicRelations: number;
87
+ /** Average prediction error (0-1, lower = better calibrated) */
88
+ predictionError: number;
89
+ };
90
+ "delegate-continuity": LayerHealthDetail & {
91
+ /** External continuity contract connected */
92
+ externalContinuityConnected: boolean;
93
+ /** Number of pending exports waiting for Thronglets */
94
+ pendingExports: number;
95
+ /** Writeback calibration loop active */
96
+ writebackLoopActive: boolean;
97
+ /** Last calibration effect breakdown */
98
+ calibrationEffects: {
99
+ converging: number;
100
+ holding: number;
101
+ diverging: number;
102
+ };
103
+ };
104
+ "policy-orchestration": LayerHealthDetail;
105
+ "public-truth": LayerHealthDetail;
106
+ }
57
107
  export declare function runHealthCheck(state: PsycheState): DiagnosticIssue[];
108
+ export declare function computeLayerHealthSummary(state: PsycheState, issues: DiagnosticIssue[]): LayerHealthSummary;
58
109
  export declare class DiagnosticCollector {
59
110
  private metrics;
60
111
  private prevChemistry;