psyche-ai 9.2.9 → 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,6 +337,16 @@ Psyche 现在更准确的目标,不是“模仿某种像人的风格”,而
326
337
 
327
338
  当前主线里,`processInput()` 会直接返回 `replyEnvelope` 作为规范主接口;原有平级字段继续保留为兼容别名,避免打断现有宿主。`policyModifiers` 不再属于规范主接口,只保留为 legacy raw vector。
328
339
 
340
+ 在这个规范主接口之外,`processInput()` 现在还会可选返回一个很薄的 `observability` side-channel。它不是新的控制面,也不会反向改写 `replyEnvelope`;它只回答五件事:
341
+
342
+ - 这轮当前由哪一个控制平面主导
343
+ - 当前 turn / writeback / session bridge / persisted relationship 四层是怎么对账的
344
+ - 这次 `work/private` 选择为什么会落在这个 profile 上
345
+ - 这轮和上一轮、session bridge、writeback、external continuity 之间的因果引用链是什么
346
+ - 当前 low-frequency external continuity 事件如何映射成 `localTraceRefs / signalRefs / traceRefs / summaryCandidateRefs`
347
+
348
+ 这样别的 agent 或宿主不用再自己猜“现在到底是谁在主导”和“为什么这次选了这个策略”,但主链路仍然只有 `replyEnvelope` 一个真相源。
349
+
329
350
  ### 内在世界
330
351
 
331
352
  Psyche 给 AI 一个始终运转的内在自我——不是条件触发,而是每时每刻都在:
@@ -547,7 +568,7 @@ Psyche 核心引擎永久开源(MIT)。
547
568
  ```bash
548
569
  npm install
549
570
  npm run build
550
- npm test # 1308 tests
571
+ npm test # 1316 tests
551
572
  npm run typecheck # strict mode
552
573
  ```
553
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
@@ -1,4 +1,4 @@
1
- import type { PsycheState, StimulusType, Locale, MBTIType, OutcomeScore, PsycheMode, PersonalityTraits, PolicyModifiers, ClassifierProvider, SubjectivityKernel, ResponseContract, GenerationControls, SessionBridgeState, ThrongletsExport, WritebackCalibrationFeedback, WritebackSignalType, ExternalContinuityEnvelope } from "./types.js";
1
+ import type { PsycheState, StimulusType, Locale, MBTIType, OutcomeScore, PsycheMode, PersonalityTraits, PolicyModifiers, ClassifierProvider, SubjectivityKernel, ResponseContract, GenerationControls, SessionBridgeState, ThrongletsExport, TurnObservability, WritebackCalibrationFeedback, WritebackSignalType, ExternalContinuityEnvelope } from "./types.js";
2
2
  import type { StorageAdapter } from "./storage.js";
3
3
  import type { DiagnosticReport, SessionMetrics } from "./diagnostics.js";
4
4
  import type { ReplyEnvelope } from "./reply-envelope.js";
@@ -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;
@@ -57,6 +57,8 @@ export interface ProcessInputResult {
57
57
  externalContinuity?: ExternalContinuityEnvelope<ThrongletsExport>;
58
58
  /** v9.2.8: sparse low-frequency export surface suitable for Thronglets */
59
59
  throngletsExports?: ThrongletsExport[];
60
+ /** v9.2.10: low-cost side channel for control boundary, state layers, and output attribution */
61
+ observability?: TurnObservability;
60
62
  /**
61
63
  * Legacy compatibility alias: ready-to-use prompt fragment summarizing raw policy modifiers.
62
64
  *
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";
@@ -28,6 +28,7 @@ import { runReflectiveTurnPhases } from "./input-turn.js";
28
28
  import { applyRelationalTurn, applySessionBridge, applyWritebackSignals, createWritebackCalibrations, evaluateWritebackCalibrations } from "./relation-dynamics.js";
29
29
  import { buildExternalContinuityEnvelope } from "./external-continuity.js";
30
30
  import { deriveThrongletsExports } from "./thronglets-export.js";
31
+ import { buildTurnObservability } from "./observability.js";
31
32
  function formatWritebackFeedbackNote(feedback, locale) {
32
33
  const top = feedback?.[0];
33
34
  if (!top)
@@ -296,7 +297,7 @@ export class PsycheEngine {
296
297
  };
297
298
  }
298
299
  // v9: Energy budget recovery during absence + depletion per turn
299
- const isExtravert = this.cfg.mbti.startsWith("E");
300
+ const isExtravert = state.baseline.DA >= 55;
300
301
  const currentBudgets = state.energyBudgets ?? { ...DEFAULT_ENERGY_BUDGETS };
301
302
  let energyBudgets = minutesElapsed >= 5
302
303
  ? computeEnergyRecovery(currentBudgets, minutesElapsed, isExtravert)
@@ -336,7 +337,7 @@ export class PsycheEngine {
336
337
  const preStimulus = current;
337
338
  // Feed drives from stimulus, then apply stimulus with drive-modified sensitivity
338
339
  drives = feedDrives(drives, primary.type);
339
- const effectiveSensitivity = computeEffectiveSensitivity(getSensitivity(state.mbti), drives, primary.type, state.traitDrift);
340
+ const effectiveSensitivity = computeEffectiveSensitivity((state.sensitivity ?? 1.0), drives, primary.type, state.traitDrift);
340
341
  // v9.2: Confidence modulates intensity — a 0.95 life-or-death dilemma
341
342
  // hits ~1.7x harder than a 0.55 mild disagreement.
342
343
  // Maps [0.5, 1.0] → [0.6, 1.2] via linear interpolation.
@@ -422,7 +423,7 @@ export class PsycheEngine {
422
423
  // ── Generate prediction for next turn's auto-learning ────
423
424
  if (appliedStimulus) {
424
425
  const ctxHash = computeContextHash(state, opts?.userId);
425
- const effectiveSensitivity = computeEffectiveSensitivity(getSensitivity(state.mbti), state.drives, appliedStimulus, state.traitDrift);
426
+ const effectiveSensitivity = computeEffectiveSensitivity((state.sensitivity ?? 1.0), state.drives, appliedStimulus, state.traitDrift);
426
427
  const predicted = predictChemistry(preInteractionState.current, appliedStimulus, state.learning, ctxHash, effectiveSensitivity, this.cfg.maxChemicalDelta);
427
428
  this.pendingPrediction = {
428
429
  predictedChemistry: predicted,
@@ -479,30 +480,24 @@ export class PsycheEngine {
479
480
  subjectivityContext: derivedReplyEnvelope.subjectivityContext,
480
481
  responseContractContext: derivedReplyEnvelope.responseContractContext,
481
482
  policyContext: derivedReplyEnvelope.policyContext || undefined,
483
+ sessionBridge,
482
484
  };
483
- if (this.cfg.compactMode) {
484
- const externalContinuity = buildExternalContinuityEnvelope(throngletsExports);
485
- return {
486
- systemContext: "",
487
- dynamicContext: buildCompactContext(state, opts?.userId, promptRenderInputs),
488
- stimulus: appliedStimulus,
489
- stimulusConfidence: this.lastStimulusAssessment?.confidence,
490
- replyEnvelope,
491
- policyModifiers: derivedReplyEnvelope.policyModifiers,
492
- subjectivityKernel: replyEnvelope.subjectivityKernel,
493
- responseContract: replyEnvelope.responseContract,
494
- generationControls: replyEnvelope.generationControls,
495
- sessionBridge,
496
- writebackFeedback,
497
- externalContinuity,
498
- throngletsExports,
499
- policyContext: derivedReplyEnvelope.policyContext,
500
- };
501
- }
485
+ const observability = buildTurnObservability(state, {
486
+ replyEnvelope,
487
+ promptRenderInputs,
488
+ compactMode: this.cfg.compactMode,
489
+ stimulus: appliedStimulus,
490
+ userText: text || undefined,
491
+ sessionBridge,
492
+ writebackFeedback,
493
+ relationContext: relationalTurn.relationContext,
494
+ externalContinuityEvents: throngletsExports,
495
+ });
496
+ // v10: compact mode is always on. Legacy buildDynamicContext removed from engine path.
502
497
  const externalContinuity = buildExternalContinuityEnvelope(throngletsExports);
503
498
  return {
504
- systemContext: this.getProtocol(locale),
505
- dynamicContext: buildDynamicContext(state, opts?.userId, promptRenderInputs),
499
+ systemContext: "",
500
+ dynamicContext: buildCompactContext(state, opts?.userId, promptRenderInputs),
506
501
  stimulus: appliedStimulus,
507
502
  stimulusConfidence: this.lastStimulusAssessment?.confidence,
508
503
  replyEnvelope,
@@ -514,6 +509,7 @@ export class PsycheEngine {
514
509
  writebackFeedback,
515
510
  externalContinuity,
516
511
  throngletsExports,
512
+ observability,
517
513
  policyContext: derivedReplyEnvelope.policyContext,
518
514
  };
519
515
  }
@@ -545,7 +541,7 @@ export class PsycheEngine {
545
541
  const selfFeedbackRate = 0.3;
546
542
  state = {
547
543
  ...state,
548
- current: applyContagion(state.current, selfPrimary.type, selfFeedbackRate, getSensitivity(state.mbti)),
544
+ current: applyContagion(state.current, selfPrimary.type, selfFeedbackRate, (state.sensitivity ?? 1.0)),
549
545
  };
550
546
  stateChanged = true;
551
547
  // v9.2 P4: Autonomic recovery — expressing vulnerable/comforting emotions
@@ -588,7 +584,7 @@ export class PsycheEngine {
588
584
  ...state,
589
585
  drives: feedDrives(state.drives, parseResult.llmStimulus),
590
586
  };
591
- 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);
592
588
  state = {
593
589
  ...state,
594
590
  current: applyStimulus(state.current, parseResult.llmStimulus, effectiveSensitivity * (overrideAllowed && this._lastAlgorithmApplied ? 0.8 : 1), this.cfg.maxChemicalDelta, NOOP_LOGGER),
@@ -772,14 +768,15 @@ export class PsycheEngine {
772
768
  }
773
769
  createDefaultState() {
774
770
  const { mbti, name, locale } = this.cfg;
775
- // Use Big Five traits if provided, otherwise use MBTI baseline
771
+ // Use Big Five traits if provided, otherwise use preset baseline
776
772
  const baseline = this.traits ? traitsToBaseline(this.traits).baseline : getBaseline(mbti);
773
+ const sensitivity = this.traits ? 1.0 : getSensitivity(mbti);
777
774
  const selfModel = getDefaultSelfModel(mbti);
778
775
  const now = new Date().toISOString();
779
776
  return {
780
- version: 9,
781
- mbti,
777
+ version: 10,
782
778
  baseline,
779
+ sensitivity,
783
780
  current: { ...baseline },
784
781
  drives: { ...DEFAULT_DRIVES },
785
782
  updatedAt: now,
@@ -824,7 +821,7 @@ export class PsycheEngine {
824
821
  */
825
822
  async resetState(opts) {
826
823
  let state = this.ensureInitialized();
827
- const baseline = this.traits ? traitsToBaseline(this.traits).baseline : getBaseline(state.mbti);
824
+ const baseline = this.traits ? traitsToBaseline(this.traits).baseline : { ...state.baseline };
828
825
  state = {
829
826
  ...state,
830
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;