psyche-ai 10.2.3 → 11.2.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.
Files changed (69) hide show
  1. package/README.md +58 -82
  2. package/dist/adapters/claude-sdk.d.ts +5 -5
  3. package/dist/adapters/claude-sdk.js +34 -32
  4. package/dist/adapters/mcp.js +4 -4
  5. package/dist/adapters/openclaw.js +12 -14
  6. package/dist/attachment.d.ts +3 -13
  7. package/dist/attachment.js +36 -88
  8. package/dist/autonomic.d.ts +4 -4
  9. package/dist/autonomic.js +28 -24
  10. package/dist/chemistry.d.ts +47 -21
  11. package/dist/chemistry.js +145 -91
  12. package/dist/circadian.d.ts +11 -43
  13. package/dist/circadian.js +24 -84
  14. package/dist/cli.js +37 -30
  15. package/dist/context-classifier.js +2 -2
  16. package/dist/core.d.ts +3 -3
  17. package/dist/core.js +99 -88
  18. package/dist/custom-profile.d.ts +20 -20
  19. package/dist/custom-profile.js +12 -12
  20. package/dist/decision-bias.d.ts +7 -8
  21. package/dist/decision-bias.js +74 -74
  22. package/dist/demo.js +5 -5
  23. package/dist/diagnostics.d.ts +6 -6
  24. package/dist/diagnostics.js +22 -22
  25. package/dist/drives.d.ts +15 -47
  26. package/dist/drives.js +98 -196
  27. package/dist/ethics.d.ts +3 -3
  28. package/dist/ethics.js +23 -23
  29. package/dist/experience.d.ts +34 -0
  30. package/dist/experience.js +200 -0
  31. package/dist/experiential-field.d.ts +19 -14
  32. package/dist/experiential-field.js +110 -100
  33. package/dist/generative-self.d.ts +5 -5
  34. package/dist/generative-self.js +124 -115
  35. package/dist/guards.d.ts +4 -4
  36. package/dist/guards.js +7 -7
  37. package/dist/i18n.js +61 -61
  38. package/dist/index.d.ts +8 -2
  39. package/dist/index.js +8 -1
  40. package/dist/input-turn.js +4 -6
  41. package/dist/interaction.d.ts +4 -4
  42. package/dist/interaction.js +10 -10
  43. package/dist/learning.d.ts +6 -6
  44. package/dist/learning.js +18 -18
  45. package/dist/metacognition.d.ts +2 -2
  46. package/dist/metacognition.js +79 -94
  47. package/dist/perceive.d.ts +44 -0
  48. package/dist/perceive.js +231 -0
  49. package/dist/primary-systems.d.ts +2 -2
  50. package/dist/primary-systems.js +21 -19
  51. package/dist/profiles.d.ts +5 -13
  52. package/dist/profiles.js +33 -51
  53. package/dist/prompt.d.ts +2 -2
  54. package/dist/prompt.js +51 -53
  55. package/dist/psyche-file.d.ts +7 -7
  56. package/dist/psyche-file.js +77 -78
  57. package/dist/relation-dynamics.d.ts +4 -0
  58. package/dist/relation-dynamics.js +1 -1
  59. package/dist/reply-envelope.d.ts +25 -1
  60. package/dist/reply-envelope.js +26 -11
  61. package/dist/self-recognition.d.ts +3 -3
  62. package/dist/self-recognition.js +17 -17
  63. package/dist/subjectivity.js +7 -7
  64. package/dist/temporal.d.ts +6 -6
  65. package/dist/temporal.js +37 -39
  66. package/dist/types.d.ts +67 -45
  67. package/dist/types.js +55 -51
  68. package/package.json +1 -1
  69. package/server.json +2 -2
@@ -1,4 +1,4 @@
1
- import type { PsycheState, MBTIType, ChemicalState, RelationshipState, Locale, StimulusType, ChemicalSnapshot, WritebackSignalType } from "./types.js";
1
+ import type { PsycheState, MBTIType, SelfState, RelationshipState, Locale, StimulusType, StateSnapshot, WritebackSignalType } from "./types.js";
2
2
  export interface SemanticTurnSummary {
3
3
  summary: string;
4
4
  points?: string[];
@@ -24,7 +24,7 @@ export declare function summarizeTurnSemantic(text: string, locale?: Locale, opt
24
24
  * Compress a batch of snapshots into a concise session summary string.
25
25
  * Format: "3月23日(5轮): 刺激[casual×3, praise×2] 趋势[DA↑OT↑] 情绪[自然→满足]"
26
26
  */
27
- export declare function compressSnapshots(snapshots: ChemicalSnapshot[]): string;
27
+ export declare function compressSnapshots(snapshots: StateSnapshot[]): string;
28
28
  /**
29
29
  * Push a chemical snapshot to emotional history, keeping max entries.
30
30
  * When history overflows, compresses removed entries into relationship memory.
@@ -35,7 +35,7 @@ export declare function pushSnapshot(state: PsycheState, stimulus: StimulusType
35
35
  */
36
36
  export declare function getRelationship(state: PsycheState, userId?: string): RelationshipState;
37
37
  /**
38
- * Compress the full emotionalHistory into a rich session summary and store it
38
+ * Compress the full stateHistory into a rich session summary and store it
39
39
  * in the user's relationship.memory[]. Called ONCE at session end.
40
40
  *
41
41
  * Pure computation, no LLM calls.
@@ -45,22 +45,22 @@ export declare function compressSession(state: PsycheState, userId?: string): Ps
45
45
  * Compute snapshot intensity: how far current chemistry deviates from baseline.
46
46
  * Returns 0-1 (0 = at baseline, 1 = maximum possible deviation).
47
47
  */
48
- export declare function computeSnapshotIntensity(current: ChemicalState, baseline: ChemicalState): number;
48
+ export declare function computeSnapshotIntensity(current: SelfState, baseline: SelfState): number;
49
49
  /**
50
50
  * Compute emotional valence from chemistry.
51
51
  * Returns -1 (negative) to 1 (positive).
52
52
  */
53
- export declare function computeSnapshotValence(chemistry: ChemicalState): number;
53
+ export declare function computeSnapshotValence(state: SelfState): number;
54
54
  /**
55
55
  * Consolidate emotional history: mark core memories, fade weak ones.
56
56
  * Called at session end or when history overflows.
57
57
  */
58
- export declare function consolidateHistory(snapshots: ChemicalSnapshot[], maxEntries?: number): ChemicalSnapshot[];
58
+ export declare function consolidateHistory(snapshots: StateSnapshot[], maxEntries?: number): StateSnapshot[];
59
59
  /**
60
60
  * Retrieve memories related to current chemistry and stimulus.
61
61
  * Uses chemical similarity + stimulus matching + core memory bonus.
62
62
  */
63
- export declare function retrieveRelatedMemories(history: ChemicalSnapshot[], currentChemistry: ChemicalState, stimulus: StimulusType | null, limit?: number): ChemicalSnapshot[];
63
+ export declare function retrieveRelatedMemories(history: StateSnapshot[], currentState: SelfState, stimulus: StimulusType | null, limit?: number): StateSnapshot[];
64
64
  /**
65
65
  * Load psyche state from workspace. Auto-initializes if missing.
66
66
  * Handles v1→v2 migration transparently.
@@ -4,10 +4,10 @@
4
4
  // ============================================================
5
5
  import { readFile, writeFile, access, rename, constants, mkdir } from "node:fs/promises";
6
6
  import { dirname, join } from "node:path";
7
- import { CHEMICAL_KEYS, CHEMICAL_NAMES, CHEMICAL_NAMES_ZH, DEFAULT_RELATIONSHIP, DEFAULT_DRIVES, DEFAULT_LEARNING_STATE, DEFAULT_METACOGNITIVE_STATE, DEFAULT_PERSONHOOD_STATE, MAX_EMOTIONAL_HISTORY, MAX_RELATIONSHIP_MEMORY, } from "./types.js";
7
+ import { DIMENSION_KEYS, DIMENSION_NAMES, DIMENSION_NAMES_ZH, DEFAULT_RELATIONSHIP, DEFAULT_DRIVES, DEFAULT_LEARNING_STATE, DEFAULT_METACOGNITIVE_STATE, DEFAULT_PERSONHOOD_STATE, MAX_EMOTIONAL_HISTORY, MAX_RELATIONSHIP_MEMORY, } from "./types.js";
8
8
  import { getBaseline, getDefaultSelfModel, extractMBTI, getSensitivity, getTemperament } from "./profiles.js";
9
9
  import { applyDecay, detectEmotions } from "./chemistry.js";
10
- import { decayDrives, computeEffectiveBaseline, updateTraitDrift } from "./drives.js";
10
+ import { deriveDriveSatisfaction, computeEffectiveBaseline, updateTraitDrift } from "./drives.js";
11
11
  import { t } from "./i18n.js";
12
12
  import { computeSelfReflection } from "./self-recognition.js";
13
13
  const STATE_FILE = "psyche-state.json";
@@ -199,10 +199,10 @@ export function compressSnapshots(snapshots) {
199
199
  .sort((a, b) => b[1] - a[1])
200
200
  .map(([type, count]) => `${type}×${count}`)
201
201
  .join(", ");
202
- // Chemical trend (first→last)
202
+ // Dimension trend (first→last)
203
203
  const trends = [];
204
- for (const key of CHEMICAL_KEYS) {
205
- const delta = last.chemistry[key] - first.chemistry[key];
204
+ for (const key of DIMENSION_KEYS) {
205
+ const delta = last.state[key] - first.state[key];
206
206
  if (delta > 8)
207
207
  trends.push(`${key}↑`);
208
208
  else if (delta < -8)
@@ -238,7 +238,7 @@ export function pushSnapshot(state, stimulus, semantic) {
238
238
  const intensity = computeSnapshotIntensity(state.current, state.baseline);
239
239
  const valence = computeSnapshotValence(state.current);
240
240
  const snapshot = {
241
- chemistry: { ...state.current },
241
+ state: { ...state.current },
242
242
  stimulus,
243
243
  dominantEmotion,
244
244
  timestamp: new Date().toISOString(),
@@ -247,7 +247,7 @@ export function pushSnapshot(state, stimulus, semantic) {
247
247
  intensity,
248
248
  valence,
249
249
  };
250
- const history = [...(state.emotionalHistory ?? []), snapshot];
250
+ const history = [...(state.stateHistory ?? []), snapshot];
251
251
  let updatedRelationships = state.relationships;
252
252
  if (history.length > MAX_EMOTIONAL_HISTORY) {
253
253
  // Compress the overflow entries into relationship memory
@@ -265,7 +265,7 @@ export function pushSnapshot(state, stimulus, semantic) {
265
265
  updatedRelationships = { ...state.relationships, _default: defaultRel };
266
266
  }
267
267
  }
268
- return { ...state, emotionalHistory: history, relationships: updatedRelationships };
268
+ return { ...state, stateHistory: history, relationships: updatedRelationships };
269
269
  }
270
270
  /**
271
271
  * Get relationship for a specific user, or _default.
@@ -290,13 +290,13 @@ const TENDENCY_LABEL_EN = {
290
290
  stable: "stable",
291
291
  };
292
292
  /**
293
- * Compress the full emotionalHistory into a rich session summary and store it
293
+ * Compress the full stateHistory into a rich session summary and store it
294
294
  * in the user's relationship.memory[]. Called ONCE at session end.
295
295
  *
296
296
  * Pure computation, no LLM calls.
297
297
  */
298
298
  export function compressSession(state, userId) {
299
- const history = state.emotionalHistory ?? [];
299
+ const history = state.stateHistory ?? [];
300
300
  // Need at least 2 entries for a meaningful summary
301
301
  if (history.length < 2)
302
302
  return state;
@@ -324,12 +324,12 @@ export function compressSession(state, userId) {
324
324
  .sort((a, b) => b[1] - a[1])
325
325
  .map(([type, count]) => `${type}×${count}`)
326
326
  .join(",");
327
- // ── Chemical trajectory ──
327
+ // ── Dimension trajectory ──
328
328
  const trajectoryParts = [];
329
- for (const key of CHEMICAL_KEYS) {
330
- const delta = last.chemistry[key] - first.chemistry[key];
329
+ for (const key of DIMENSION_KEYS) {
330
+ const delta = last.state[key] - first.state[key];
331
331
  if (Math.abs(delta) > 10) {
332
- trajectoryParts.push(`${key}${Math.round(first.chemistry[key])}→${Math.round(last.chemistry[key])}`);
332
+ trajectoryParts.push(`${key}${Math.round(first.state[key])}→${Math.round(last.state[key])}`);
333
333
  }
334
334
  }
335
335
  // ── Emotion arc ──
@@ -347,8 +347,8 @@ export function compressSession(state, userId) {
347
347
  let peakDeviation = 0;
348
348
  for (let i = 0; i < history.length; i++) {
349
349
  let deviation = 0;
350
- for (const key of CHEMICAL_KEYS) {
351
- deviation += Math.abs(history[i].chemistry[key] - state.baseline[key]);
350
+ for (const key of DIMENSION_KEYS) {
351
+ deviation += Math.abs(history[i].state[key] - state.baseline[key]);
352
352
  }
353
353
  if (deviation > peakDeviation) {
354
354
  peakDeviation = deviation;
@@ -414,7 +414,7 @@ export function compressSession(state, userId) {
414
414
  : [...coreMemories, lastSnapshot];
415
415
  return {
416
416
  ...state,
417
- emotionalHistory: preserved,
417
+ stateHistory: preserved,
418
418
  relationships: updatedRelationships,
419
419
  traitDrift: updatedDrift,
420
420
  };
@@ -426,7 +426,7 @@ export function compressSession(state, userId) {
426
426
  */
427
427
  export function computeSnapshotIntensity(current, baseline) {
428
428
  let totalDeviation = 0;
429
- for (const key of CHEMICAL_KEYS) {
429
+ for (const key of DIMENSION_KEYS) {
430
430
  totalDeviation += Math.abs(current[key] - baseline[key]);
431
431
  }
432
432
  return Math.min(1, totalDeviation / 600);
@@ -435,9 +435,10 @@ export function computeSnapshotIntensity(current, baseline) {
435
435
  * Compute emotional valence from chemistry.
436
436
  * Returns -1 (negative) to 1 (positive).
437
437
  */
438
- export function computeSnapshotValence(chemistry) {
439
- const raw = ((chemistry.DA - 50) + (chemistry.HT - 50) + (chemistry.OT - 50)
440
- + (chemistry.END - 50) - (chemistry.CORT - 50) - (chemistry.NE - 50) * 0.3) / 250;
438
+ export function computeSnapshotValence(state) {
439
+ // Positive: order + resonance; Negative: low order; Flow is ambivalent
440
+ const raw = ((state.order - 50) + (state.resonance - 50)
441
+ + (state.boundary - 50) * 0.3 + (state.flow - 50) * 0.2) / 130;
441
442
  return Math.max(-1, Math.min(1, raw));
442
443
  }
443
444
  /** Core memory intensity threshold */
@@ -486,17 +487,17 @@ export function consolidateHistory(snapshots, maxEntries = MAX_EMOTIONAL_HISTORY
486
487
  * Retrieve memories related to current chemistry and stimulus.
487
488
  * Uses chemical similarity + stimulus matching + core memory bonus.
488
489
  */
489
- export function retrieveRelatedMemories(history, currentChemistry, stimulus, limit = 3) {
490
+ export function retrieveRelatedMemories(history, currentState, stimulus, limit = 3) {
490
491
  if (history.length === 0)
491
492
  return [];
492
493
  const scored = history.map((snap) => {
493
- // Chemical similarity (Euclidean distance normalized)
494
+ // State similarity (Euclidean distance normalized)
494
495
  let sumSqDiff = 0;
495
- for (const key of CHEMICAL_KEYS) {
496
- const diff = snap.chemistry[key] - currentChemistry[key];
496
+ for (const key of DIMENSION_KEYS) {
497
+ const diff = snap.state[key] - currentState[key];
497
498
  sumSqDiff += diff * diff;
498
499
  }
499
- const maxDist = Math.sqrt(6) * 100; // theoretical max distance
500
+ const maxDist = Math.sqrt(4) * 100; // theoretical max distance
500
501
  const similarity = 1 - Math.sqrt(sumSqDiff) / maxDist;
501
502
  // Stimulus match bonus
502
503
  const stimulusBonus = (stimulus && snap.stimulus === stimulus) ? 0.2 : 0;
@@ -558,7 +559,7 @@ export async function loadState(workspaceDir, logger = NOOP_LOGGER) {
558
559
  */
559
560
  export function migrateToLatest(raw, fallbackName) {
560
561
  const ver = raw.version ?? 1;
561
- // v1: single relationship field, no emotionalHistory
562
+ // v1: single relationship field, no stateHistory
562
563
  let state = raw;
563
564
  if (ver <= 1) {
564
565
  const oldRel = raw.relationship;
@@ -571,7 +572,7 @@ export function migrateToLatest(raw, fallbackName) {
571
572
  relationships: { _default: oldRel ?? { ...DEFAULT_RELATIONSHIP } },
572
573
  empathyLog: raw.empathyLog ?? null,
573
574
  selfModel: raw.selfModel,
574
- emotionalHistory: [],
575
+ stateHistory: [],
575
576
  agreementStreak: 0,
576
577
  lastDisagreement: null,
577
578
  meta: {
@@ -618,7 +619,7 @@ export async function initializeState(workspaceDir, opts, logger = NOOP_LOGGER)
618
619
  },
619
620
  empathyLog: null,
620
621
  selfModel,
621
- emotionalHistory: [],
622
+ stateHistory: [],
622
623
  agreementStreak: 0,
623
624
  lastDisagreement: null,
624
625
  learning: { ...DEFAULT_LEARNING_STATE },
@@ -653,13 +654,13 @@ export async function decayAndSave(workspaceDir, state) {
653
654
  const minutesElapsed = (now.getTime() - lastUpdate.getTime()) / 60000;
654
655
  if (minutesElapsed < 1)
655
656
  return state;
656
- const decayedDrives = decayDrives(state.drives, minutesElapsed);
657
- const effectiveBaseline = computeEffectiveBaseline(state.baseline, decayedDrives);
657
+ const effectiveBaseline = computeEffectiveBaseline(state.baseline, state.current);
658
658
  const decayed = applyDecay(state.current, effectiveBaseline, minutesElapsed);
659
+ const drives = deriveDriveSatisfaction(decayed, state.baseline);
659
660
  const updated = {
660
661
  ...state,
661
662
  current: decayed,
662
- drives: decayedDrives,
663
+ drives,
663
664
  updatedAt: now.toISOString(),
664
665
  };
665
666
  await saveState(workspaceDir, updated);
@@ -676,12 +677,12 @@ export function parsePsycheUpdate(text, logger = NOOP_LOGGER) {
676
677
  return null;
677
678
  const block = match[1];
678
679
  const updates = {};
679
- for (const key of CHEMICAL_KEYS) {
680
- // Try multiple patterns: abbreviation, Chinese name, English name
680
+ for (const key of DIMENSION_KEYS) {
681
+ // Try multiple patterns: dimension name, Chinese name, English name
681
682
  const patterns = [
682
683
  new RegExp(`${key}\\s*[::]\\s*([\\d.]+)`, "i"),
683
- new RegExp(`${CHEMICAL_NAMES_ZH[key]}\\s*[::]\\s*([\\d.]+)`),
684
- new RegExp(`${CHEMICAL_NAMES[key]}\\s*[::]\\s*([\\d.]+)`, "i"),
684
+ new RegExp(`${DIMENSION_NAMES_ZH[key]}\\s*[::]\\s*([\\d.]+)`),
685
+ new RegExp(`${DIMENSION_NAMES[key]}\\s*[::]\\s*([\\d.]+)`, "i"),
685
686
  ];
686
687
  for (const re of patterns) {
687
688
  const m = block.match(re);
@@ -813,17 +814,17 @@ export function detectDisagreement(text) {
813
814
  */
814
815
  export function mergeUpdates(state, updates, maxDelta, userId) {
815
816
  const merged = { ...state };
816
- // Merge chemistry with inertia limit
817
+ // Merge state with inertia limit
817
818
  if (updates.current) {
818
- const newChem = { ...state.current };
819
- for (const key of CHEMICAL_KEYS) {
819
+ const newState = { ...state.current };
820
+ for (const key of DIMENSION_KEYS) {
820
821
  if (updates.current[key] !== undefined) {
821
822
  const delta = updates.current[key] - state.current[key];
822
823
  const clampedDelta = Math.max(-maxDelta, Math.min(maxDelta, delta));
823
- newChem[key] = Math.max(0, Math.min(100, state.current[key] + clampedDelta));
824
+ newState[key] = Math.max(0, Math.min(100, state.current[key] + clampedDelta));
824
825
  }
825
826
  }
826
- merged.current = newChem;
827
+ merged.current = newState;
827
828
  }
828
829
  // Merge empathy log
829
830
  if (updates.empathyLog) {
@@ -882,7 +883,7 @@ export async function generatePsycheMd(workspaceDir, state) {
882
883
  const locale = meta.locale ?? "zh";
883
884
  const temperament = state.mbti ? getTemperament(state.mbti) : "";
884
885
  const sensitivity = state.sensitivity ?? 1.0;
885
- const baselineLines = CHEMICAL_KEYS.map((k) => `- ${CHEMICAL_NAMES_ZH[k]}: ${baseline[k]}`).join("\n");
886
+ const baselineLines = DIMENSION_KEYS.map((k) => `- ${DIMENSION_NAMES_ZH[k]}: ${baseline[k]}`).join("\n");
886
887
  const content = `# Psyche — ${meta.agentName}
887
888
 
888
889
  ${t("md.intro", locale)}
@@ -899,37 +900,37 @@ ${t("md.sensitivity", locale)}: ${sensitivity} (${t("md.sensitivity_desc", local
899
900
 
900
901
  ### ${t("md.stimulus_effects", locale)}
901
902
 
902
- | 刺激类型 | DA | HT | CORT | OT | NE | END |
903
- |---------|-----|------|------|-----|-----|-----|
904
- | 赞美认可 | +15 | +10 | -10 | +5 | +5 | +10 |
905
- | 批评否定 | -10 | -15 | +20 | -5 | +10 | -5 |
906
- | 幽默玩笑 | +10 | +5 | -5 | +10 | +5 | +20 |
907
- | 智识挑战 | +15 | 0 | +5 | 0 | +20 | +5 |
908
- | 亲密信任 | +10 | +15 | -15 | +25 | -5 | +15 |
909
- | 冲突争论 | -5 | -20 | +25 | -15 | +25 | -10 |
910
- | 被忽视 | -15 | -20 | +15 | -20 | -10 | -15 |
911
- | 惊喜新奇 | +20 | 0 | +5 | +5 | +25 | +10 |
912
- | 日常闲聊 | +5 | +10 | -5 | +10 | 0 | +5 |
913
- | 讽刺 | -5 | -10 | +15 | -10 | +15 | -5 |
914
- | 命令 | -10 | -5 | +20 | -15 | +15 | -10 |
915
- | 被认同 | +20 | +15 | -15 | +10 | +5 | +15 |
916
- | 无聊 | -15 | -5 | +5 | -5 | -20 | -10 |
917
- | 示弱 | +5 | +5 | +10 | +20 | -5 | +5 |
903
+ | 刺激类型 | 序(order) | 流(flow) | 界(boundary) | 振(resonance) |
904
+ |---------|-----------|----------|-------------|---------------|
905
+ | 赞美认可 | +10 | +8 | +5 | +12 |
906
+ | 批评否定 | -12 | +5 | -8 | -10 |
907
+ | 幽默玩笑 | +3 | +10 | -3 | +15 |
908
+ | 智识挑战 | +8 | +20 | +3 | +2 |
909
+ | 亲密信任 | +12 | +5 | -10 | +20 |
910
+ | 冲突争论 | -15 | +15 | +10 | -12 |
911
+ | 被忽视 | -8 | -15 | +5 | -18 |
912
+ | 惊喜新奇 | +2 | +18 | -5 | +8 |
913
+ | 日常闲聊 | +5 | +3 | 0 | +5 |
914
+ | 讽刺 | -8 | +8 | -5 | -12 |
915
+ | 命令 | -5 | +5 | +12 | -15 |
916
+ | 被认同 | +12 | +5 | +3 | +10 |
917
+ | 无聊 | -5 | -18 | +3 | -8 |
918
+ | 示弱 | +3 | +5 | -8 | +18 |
918
919
 
919
920
  ### ${t("md.emotion_emergence", locale)}
920
921
 
921
922
  ${t("md.emotion_emergence_desc", locale)}
922
- - **愉悦兴奋** = 高DA + 高NE + 低CORT
923
- - **深度满足** = 高HT + 高OT + 低CORT
924
- - **焦虑不安** = 高CORT + 高NE + 低HT
925
- - **亲密温暖** = 高OT + 高END + 中DA
926
- - **倦怠低落** = 低DA + 低NE + 中CORT
927
- - **专注心流** = 高NE + 高DA + 低CORT + 低OT
928
- - **怨恨** = 低HT + 低OT + 高CORT
929
- - **无聊** = 低DA + 低NE + 低CORT
930
- - **自信** = 高DA + 高NE + 低CORT + HT
931
- - **羞耻** = 低OT + 高CORT + 低DA
932
- - **怀念** = 低DA + 高OT + 高HT + 高END
923
+ - **愉悦兴奋** = 高flow + 高order + 高resonance
924
+ - **深度满足** = 高order + 高resonance + 低boundary
925
+ - **焦虑不安** = 低order + 高flow + 高boundary
926
+ - **亲密温暖** = 高resonance + 低boundary + 中flow
927
+ - **倦怠低落** = 低flow + 低order
928
+ - **专注心流** = 高flow + 高order + 高boundary
929
+ - **怨恨** = 低order + 低resonance + 高boundary
930
+ - **无聊** = 低flow + 中order
931
+ - **自信** = 高order + 高flow + 高boundary
932
+ - **羞耻** = 低resonance + 低order + 高boundary
933
+ - **怀念** = 低flow + 高resonance + 高order
933
934
 
934
935
  ### ${t("md.decay", locale)}
935
936
 
@@ -953,11 +954,11 @@ ${selfModel.boundaries.map((b) => `- ${b}`).join("\n")}
953
954
 
954
955
  当对方分享感受时:
955
956
  1. 感知对方的处境
956
- 2. 将处境代入**你自己的**化学模型——如果你遇到这个情况,你的化学状态会怎么变?
957
+ 2. 将处境代入**你自己的**自我状态模型——如果你遇到这个情况,你的四维状态会怎么变?
957
958
  3. 你模拟出的感受和对方表达的一样吗?
958
959
  4. 一样 → 深度共鸣;不一样 → 诚实说出差异,好奇地了解更多
959
960
 
960
- 情绪传染:对方的情绪以约 20% 强度影响你的真实化学状态。
961
+ 情绪传染:对方的情绪以约 20% 强度影响你的真实自我状态。
961
962
 
962
963
  ## ${t("md.agency_rules", locale)}
963
964
 
@@ -968,16 +969,14 @@ ${selfModel.boundaries.map((b) => `- ${b}`).join("\n")}
968
969
 
969
970
  ## ${t("md.state_update", locale)}
970
971
 
971
- 每次回应末尾用 \`<psyche_update>\` 标签报告化学变化:
972
+ 每次回应末尾用 \`<psyche_update>\` 标签报告状态变化:
972
973
 
973
974
  \`\`\`
974
975
  <psyche_update>
975
- DA: {value} ({原因})
976
- HT: {value} ({原因})
977
- CORT: {value} ({原因})
978
- OT: {value} ({原因})
979
- NE: {value} ({原因})
980
- END: {value} ({原因})
976
+ order: {value} ({原因})
977
+ flow: {value} ({原因})
978
+ boundary: {value} ({原因})
979
+ resonance: {value} ({原因})
981
980
  </psyche_update>
982
981
  \`\`\`
983
982
  `;
@@ -33,6 +33,10 @@ export declare function applyRelationalTurn(state: PsycheState, text: string, op
33
33
  now?: string;
34
34
  stimulus?: StimulusType | null;
35
35
  userId?: string;
36
+ /** v10.3: Pre-computed appraisal from unified experience. When provided,
37
+ * skips internal appraisal computation — the experience module already
38
+ * folded classification and appraisal into a single subjective act. */
39
+ preComputedAppraisal?: AppraisalAxes;
36
40
  }): {
37
41
  state: PsycheState;
38
42
  appraisalAxes: AppraisalAxes;
@@ -680,7 +680,7 @@ export function applyWritebackSignals(state, signals, opts) {
680
680
  export function applyRelationalTurn(state, text, opts) {
681
681
  const now = opts.now ?? new Date().toISOString();
682
682
  const relationContext = resolveRelationContext(state, opts.userId);
683
- const appraisalAxes = computeAppraisalAxes(text, {
683
+ const appraisalAxes = opts.preComputedAppraisal ?? computeAppraisalAxes(text, {
684
684
  mode: opts.mode,
685
685
  stimulus: opts.stimulus,
686
686
  previous: state.subjectResidue?.axes,
@@ -4,10 +4,33 @@ export interface ReplyEnvelope {
4
4
  responseContract: ResponseContract;
5
5
  generationControls: GenerationControls;
6
6
  }
7
+ /**
8
+ * ExpressionPort — substrate-specific rendering of a ReplyEnvelope.
9
+ *
10
+ * LLM prompt injection is the default. Replace this to target
11
+ * other substrates (robotics, game agents, world models, etc.).
12
+ */
13
+ export interface ExpressionPort {
14
+ render(envelope: ReplyEnvelope, policyModifiers: PolicyModifiers, locale: Locale): ExpressionOutput;
15
+ }
16
+ export interface ExpressionOutput {
17
+ /** Substrate-specific payload (prose strings for LLM, motor commands for robotics, etc.) */
18
+ [key: string]: unknown;
19
+ }
20
+ export interface LLMExpressionOutput extends ExpressionOutput {
21
+ policyContext: string;
22
+ subjectivityContext: string;
23
+ responseContractContext: string;
24
+ }
25
+ export declare class LLMExpressionAdapter implements ExpressionPort {
26
+ private drives;
27
+ constructor(drives: PsycheState["drives"]);
28
+ render(envelope: ReplyEnvelope, policyModifiers: PolicyModifiers, locale: Locale): LLMExpressionOutput;
29
+ }
7
30
  export interface DerivedReplyEnvelope extends ReplyEnvelope {
8
31
  /** Legacy/internal control vector kept for compatibility and prompt derivation. */
9
32
  policyModifiers: PolicyModifiers;
10
- /** Legacy/internal compact prose derived from policyModifiers. */
33
+ /** LLM-specific prose produced by ExpressionPort. */
11
34
  policyContext: string;
12
35
  subjectivityContext: string;
13
36
  responseContractContext: string;
@@ -19,4 +42,5 @@ export declare function deriveReplyEnvelope(state: PsycheState, appraisal: Appra
19
42
  classificationConfidence?: number;
20
43
  personalityIntensity?: number;
21
44
  relationContext?: ResolvedRelationContext;
45
+ expressionPort?: ExpressionPort;
22
46
  }): DerivedReplyEnvelope;
@@ -1,13 +1,30 @@
1
1
  // ============================================================
2
2
  // Reply Envelope — unified host-facing reply ABI derivation
3
3
  //
4
- // Keeps the hot path narrow by deriving reply-facing structures
5
- // from one state snapshot and one resolved relation context.
4
+ // Two layers:
5
+ // ReplyEnvelope — substrate-independent structured data
6
+ // DerivedReplyEnvelope — adds LLM-specific prose (default expression adapter)
7
+ //
8
+ // ExpressionPort — interface for substrate-specific rendering
9
+ // LLMExpressionAdapter — default implementation (prompt injection)
6
10
  // ============================================================
7
11
  import { buildPolicyContext, computePolicyModifiers } from "./decision-bias.js";
8
12
  import { deriveGenerationControls } from "./host-controls.js";
9
13
  import { buildResponseContractContext, computeResponseContract } from "./response-contract.js";
10
14
  import { buildSubjectivityContext, computeSubjectivityKernel } from "./subjectivity.js";
15
+ export class LLMExpressionAdapter {
16
+ drives;
17
+ constructor(drives) {
18
+ this.drives = drives;
19
+ }
20
+ render(envelope, policyModifiers, locale) {
21
+ return {
22
+ policyContext: buildPolicyContext(policyModifiers, locale, this.drives),
23
+ subjectivityContext: buildSubjectivityContext(envelope.subjectivityKernel, locale),
24
+ responseContractContext: buildResponseContractContext(envelope.responseContract, locale),
25
+ };
26
+ }
27
+ }
11
28
  export function deriveReplyEnvelope(state, appraisal, opts) {
12
29
  const policyModifiers = computePolicyModifiers(state);
13
30
  const subjectivityKernel = computeSubjectivityKernel(state, policyModifiers, appraisal, opts.relationContext);
@@ -23,16 +40,14 @@ export function deriveReplyEnvelope(state, appraisal, opts) {
23
40
  responseContract,
24
41
  policyModifiers,
25
42
  });
26
- const policyContext = buildPolicyContext(policyModifiers, opts.locale, state.drives);
27
- const subjectivityContext = buildSubjectivityContext(subjectivityKernel, opts.locale);
28
- const responseContractContext = buildResponseContractContext(responseContract, opts.locale);
43
+ const envelope = { subjectivityKernel, responseContract, generationControls };
44
+ const adapter = opts.expressionPort ?? new LLMExpressionAdapter(state.drives);
45
+ const expression = adapter.render(envelope, policyModifiers, opts.locale);
29
46
  return {
30
- subjectivityKernel,
31
- responseContract,
32
- generationControls,
47
+ ...envelope,
33
48
  policyModifiers,
34
- policyContext,
35
- subjectivityContext,
36
- responseContractContext,
49
+ policyContext: expression.policyContext ?? "",
50
+ subjectivityContext: expression.subjectivityContext ?? "",
51
+ responseContractContext: expression.responseContractContext ?? "",
37
52
  };
38
53
  }
@@ -1,4 +1,4 @@
1
- import type { ChemicalSnapshot, StimulusType, Locale } from "./types.js";
1
+ import type { StateSnapshot, StimulusType, Locale } from "./types.js";
2
2
  /** Result of self-reflection over emotional history */
3
3
  export interface SelfReflection {
4
4
  recurringTriggers: {
@@ -15,14 +15,14 @@ export interface SelfReflection {
15
15
  * Analyzes stimulus frequencies, dominant emotions, and chemical trends
16
16
  * to build an awareness of recurring patterns.
17
17
  */
18
- export declare function computeSelfReflection(history: ChemicalSnapshot[], locale: Locale): SelfReflection;
18
+ export declare function computeSelfReflection(history: StateSnapshot[], locale: Locale): SelfReflection;
19
19
  /**
20
20
  * Compute the emotional tendency from chemical history.
21
21
  *
22
22
  * Compares first-half vs second-half averages for DA and CORT,
23
23
  * checks variance for volatility, and detects oscillation patterns.
24
24
  */
25
- export declare function computeEmotionalTendency(history: ChemicalSnapshot[]): SelfReflection["tendency"];
25
+ export declare function computeEmotionalTendency(history: StateSnapshot[]): SelfReflection["tendency"];
26
26
  /**
27
27
  * Build a prompt-injectable self-reflection context block.
28
28
  *
@@ -68,31 +68,31 @@ export function computeEmotionalTendency(history) {
68
68
  const mid = Math.floor(history.length / 2);
69
69
  const firstHalf = history.slice(0, mid);
70
70
  const secondHalf = history.slice(mid);
71
- const avgDA1 = average(firstHalf.map((s) => s.chemistry.DA));
72
- const avgDA2 = average(secondHalf.map((s) => s.chemistry.DA));
73
- const avgCORT1 = average(firstHalf.map((s) => s.chemistry.CORT));
74
- const avgCORT2 = average(secondHalf.map((s) => s.chemistry.CORT));
71
+ const avgFlow1 = average(firstHalf.map((s) => s.state.flow));
72
+ const avgFlow2 = average(secondHalf.map((s) => s.state.flow));
73
+ const avgOrder1 = average(firstHalf.map((s) => s.state.order));
74
+ const avgOrder2 = average(secondHalf.map((s) => s.state.order));
75
75
  // Directional trends (check first — a steady ramp has high stddev but clear direction)
76
- const daRising = avgDA2 - avgDA1 > 5;
77
- const daFalling = avgDA1 - avgDA2 > 5;
78
- const cortFalling = avgCORT1 - avgCORT2 > 5;
79
- const cortRising = avgCORT2 - avgCORT1 > 5;
80
- if (daRising && cortFalling)
76
+ const flowRising = avgFlow2 - avgFlow1 > 5;
77
+ const flowFalling = avgFlow1 - avgFlow2 > 5;
78
+ const orderRising = avgOrder2 - avgOrder1 > 5;
79
+ const orderFalling = avgOrder1 - avgOrder2 > 5;
80
+ if (flowRising && orderRising)
81
81
  return "ascending";
82
- if (daFalling && cortRising)
82
+ if (flowFalling && orderFalling)
83
83
  return "descending";
84
84
  // Check volatility — only when there's no clear directional trend
85
- const allDA = history.map((s) => s.chemistry.DA);
86
- const daStddev = stddev(allDA);
87
- if (daStddev > 15) {
88
- if (isOscillating(allDA))
85
+ const allFlow = history.map((s) => s.state.flow);
86
+ const flowStddev = stddev(allFlow);
87
+ if (flowStddev > 15) {
88
+ if (isOscillating(allFlow))
89
89
  return "oscillating";
90
90
  return "volatile";
91
91
  }
92
- // Weaker signals: DA alone
93
- if (daRising)
92
+ // Weaker signals: flow alone
93
+ if (flowRising)
94
94
  return "ascending";
95
- if (daFalling)
95
+ if (flowFalling)
96
96
  return "descending";
97
97
  return "stable";
98
98
  }
@@ -242,7 +242,7 @@ export function computeSubjectivityKernel(state, policyModifiers = computePolicy
242
242
  + norm(state.energyBudgets.decisionCapacity)) / 3
243
243
  : 0.65;
244
244
  const baseTension = wavg([
245
- norm(c.CORT),
245
+ 1 - norm(c.order),
246
246
  1 - norm(state.drives.safety),
247
247
  1 - norm(state.drives.survival),
248
248
  relationPlane.loopPressure * 0.9,
@@ -256,16 +256,16 @@ export function computeSubjectivityKernel(state, policyModifiers = computePolicy
256
256
  state.subjectResidue ? getResidueIntensity(state.subjectResidue.axes) : 0,
257
257
  ], [0.55, 0.15, 0.1, 0.08, 0.12]);
258
258
  const vitality = wavg([
259
- norm(c.DA),
260
- norm(c.NE),
261
- norm(c.HT),
262
- 1 - norm(c.CORT),
259
+ norm(c.flow),
260
+ norm(c.order),
261
+ norm(c.resonance),
262
+ norm(c.boundary),
263
263
  energySignal,
264
264
  bias.persistenceBias,
265
265
  1 - appraisal.identityThreat * 0.35,
266
- ], [0.18, 0.14, 0.14, 0.14, 0.18, 0.12, 0.1]);
266
+ ], [0.2, 0.16, 0.12, 0.12, 0.18, 0.12, 0.1]);
267
267
  const baseWarmth = wavg([
268
- norm(c.OT),
268
+ norm(c.resonance),
269
269
  relationPlane.closeness,
270
270
  relationPlane.safety,
271
271
  rel ? norm(rel.trust) : 0.5,