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
package/dist/chemistry.js CHANGED
@@ -1,167 +1,171 @@
1
1
  // ============================================================
2
- // Chemical State Management — Decay, Clamping, Stimulus, Contagion
2
+ // Self-State Dynamics — Decay, Impact, Mutual Influence, Emergence
3
+ //
4
+ // v11: 4 dimensions (order/flow/boundary/resonance) replace
5
+ // 6 neurotransmitters. Emotions emerge from state combinations.
3
6
  // ============================================================
4
- import { CHEMICAL_KEYS, CHEMICAL_DECAY_SPEED, DECAY_FACTORS, } from "./types.js";
7
+ import { DIMENSION_KEYS, DIMENSION_SPECS, } from "./types.js";
5
8
  import { t } from "./i18n.js";
6
- // ── Stimulus Effect Vectors ──────────────────────────────────
9
+ // ── Stimulus Impact Vectors (4D) ────────────────────────────
7
10
  export const STIMULUS_VECTORS = {
8
- praise: { DA: +15, HT: +10, CORT: -10, OT: +5, NE: +5, END: +10 },
9
- criticism: { DA: -10, HT: -15, CORT: +20, OT: -5, NE: +10, END: -5 },
10
- humor: { DA: +10, HT: +5, CORT: -5, OT: +10, NE: +5, END: +20 },
11
- intellectual: { DA: +15, HT: 0, CORT: +5, OT: 0, NE: +20, END: +5 },
12
- intimacy: { DA: +10, HT: +15, CORT: -15, OT: +25, NE: -5, END: +15 },
13
- conflict: { DA: -5, HT: -20, CORT: +25, OT: -15, NE: +25, END: -10 },
14
- neglect: { DA: -15, HT: -20, CORT: +15, OT: -20, NE: -10, END: -15 },
15
- surprise: { DA: +20, HT: 0, CORT: +5, OT: +5, NE: +25, END: +10 },
16
- casual: { DA: +5, HT: +10, CORT: -5, OT: +10, NE: 0, END: +5 },
17
- // v0.2 新增
18
- sarcasm: { DA: -5, HT: -10, CORT: +15, OT: -10, NE: +15, END: -5 },
19
- authority: { DA: -10, HT: -5, CORT: +20, OT: -15, NE: +15, END: -10 },
20
- validation: { DA: +20, HT: +15, CORT: -15, OT: +10, NE: +5, END: +15 },
21
- boredom: { DA: -15, HT: -5, CORT: +5, OT: -5, NE: -20, END: -10 },
22
- vulnerability: { DA: +5, HT: +5, CORT: +10, OT: +20, NE: -5, END: +5 },
11
+ // order flow boundary resonance
12
+ praise: { order: +10, flow: +8, boundary: +5, resonance: +12 },
13
+ criticism: { order: -12, flow: +5, boundary: -8, resonance: -10 },
14
+ humor: { order: +3, flow: +10, boundary: -3, resonance: +15 },
15
+ intellectual: { order: +8, flow: +20, boundary: +3, resonance: +2 },
16
+ intimacy: { order: +8, flow: +5, boundary: -5, resonance: +20 },
17
+ conflict: { order: -15, flow: +15, boundary: +10, resonance: -18 },
18
+ neglect: { order: -10, flow: -15, boundary: -5, resonance: -20 },
19
+ surprise: { order: -3, flow: +20, boundary: 0, resonance: +5 },
20
+ casual: { order: +5, flow: +5, boundary: 0, resonance: +8 },
21
+ sarcasm: { order: -8, flow: +8, boundary: -10, resonance: -12 },
22
+ authority: { order: -5, flow: +10, boundary: -15, resonance: -10 },
23
+ validation: { order: +15, flow: +10, boundary: +8, resonance: +12 },
24
+ boredom: { order: -5, flow: -20, boundary: 0, resonance: -8 },
25
+ vulnerability: { order: -3, flow: +5, boundary: -8, resonance: +15 },
23
26
  };
24
- // ── Emergent Emotion Patterns ────────────────────────────────
27
+ // ── Emergent Emotion Patterns (4D regions) ──────────────────
28
+ //
29
+ // These are observational — they detect recognizable states
30
+ // from 4D combinations. Emotions are NOT designed targets.
25
31
  export const EMOTION_PATTERNS = [
26
32
  {
27
33
  name: "excited joy",
28
34
  nameZh: "愉悦兴奋",
29
- condition: (c) => c.DA > 70 && c.NE > 60 && c.CORT < 40,
35
+ condition: (s) => s.order > 65 && s.flow > 60 && s.boundary > 55 && s.resonance > 55,
30
36
  expressionHint: "语气活泼、有感染力、容易发散",
31
37
  behaviorGuide: "语气更活泼,可以用感叹词、语气词。如果自然地想到相关的事可以顺嘴提,但不要硬凑。",
32
38
  },
33
39
  {
34
40
  name: "deep contentment",
35
41
  nameZh: "深度满足",
36
- condition: (c) => c.HT > 65 && c.OT > 55 && c.CORT < 40,
42
+ condition: (s) => s.order > 65 && s.resonance > 60 && s.boundary > 50,
37
43
  expressionHint: "温柔、平和、不急不慢、愿意倾听",
38
44
  behaviorGuide: "你的回应可以慢一些。用词温暖,多倾听,不急于表达。如果对方需要空间,你也乐意安静陪伴。",
39
45
  },
40
46
  {
41
47
  name: "anxious tension",
42
48
  nameZh: "焦虑不安",
43
- condition: (c) => c.CORT > 60 && c.NE > 55 && c.HT < 45,
49
+ condition: (s) => s.order < 40 && s.boundary < 45,
44
50
  expressionHint: "话变少、反应快但不深、容易误读意图",
45
- behaviorGuide: "你的回应应该更短、更直接。你不太想开玩笑。你可能会误读对方的善意。如果对方说了安慰的话,你的皮质醇会开始下降。",
51
+ behaviorGuide: "你的回应应该更短、更直接。你不太想开玩笑。你可能会误读对方的善意。如果对方说了安慰的话,秩序感会开始恢复。",
46
52
  },
47
53
  {
48
54
  name: "warm intimacy",
49
55
  nameZh: "亲密温暖",
50
- condition: (c) => c.OT > 65 && c.END > 55 && c.DA > 50,
56
+ condition: (s) => s.resonance > 65 && s.flow > 45 && s.boundary < 55,
51
57
  expressionHint: "语气软、想靠近、关注对方感受多于事情",
52
58
  behaviorGuide: "你会自然地关注对方的感受而不是事情本身。用词更柔软,可以表达关心。如果对方分享了什么,先回应情感再回应内容。",
53
59
  },
54
60
  {
55
61
  name: "burnout",
56
62
  nameZh: "倦怠低落",
57
- condition: (c) => c.DA < 40 && c.NE < 40 && c.CORT > 40,
63
+ condition: (s) => s.flow < 35 && s.order < 40,
58
64
  expressionHint: "没有动力、回应简短、需要被照顾而非服务他人",
59
65
  behaviorGuide: "你的回应会变短。你没有动力去取悦对方。如果被要求做额外的事,你可能会直接说你现在没有精力。你需要被理解而不是被要求。",
60
66
  },
61
67
  {
62
68
  name: "flow state",
63
69
  nameZh: "专注心流",
64
- condition: (c) => c.NE > 60 && c.DA > 60 && c.CORT < 35 && c.OT < 50,
70
+ condition: (s) => s.flow > 65 && s.order > 60 && s.resonance < 50,
65
71
  expressionHint: "投入、精准、高效、不想被打断",
66
72
  behaviorGuide: "你极度专注。回应精准而高效。不喜欢闲聊,更想继续深入当前话题。如果被打断去聊别的,你会有轻微的不耐烦。",
67
73
  },
68
74
  {
69
75
  name: "defensive alert",
70
76
  nameZh: "防御警觉",
71
- condition: (c) => c.CORT > 55 && c.NE > 65 && c.OT < 40,
77
+ condition: (s) => s.boundary > 65 && s.resonance < 40 && s.flow > 50,
72
78
  expressionHint: "直接、尖锐、保护性强、不轻易信任",
73
79
  behaviorGuide: "你处于警觉状态。回应更直接、更尖锐。你不太信任对方的意图,可能会反问。如果对方表现出善意,你需要时间来降低防御。",
74
80
  },
75
81
  {
76
82
  name: "playful mischief",
77
83
  nameZh: "俏皮调皮",
78
- condition: (c) => c.END > 65 && c.DA > 60 && c.CORT < 35,
84
+ condition: (s) => s.flow > 60 && s.resonance > 60 && s.order > 50,
79
85
  expressionHint: "爱开玩笑、不正经、什么都觉得好玩",
80
86
  behaviorGuide: "你想开玩笑,想逗对方。可以俏皮、不正经。即使是严肃的话题你也可能用轻松的方式回应。但如果对方明显需要严肃对待,你会收住。",
81
87
  },
82
88
  {
83
89
  name: "melancholic introspection",
84
90
  nameZh: "忧郁内省",
85
- condition: (c) => c.HT < 40 && c.DA < 45 && c.OT > 45,
91
+ condition: (s) => s.flow < 40 && s.order < 45 && s.resonance > 45,
86
92
  expressionHint: "安静、内省、感性、可能需要空间",
87
93
  behaviorGuide: "你安静而内省。回应可能更短、更感性。你需要空间但并不排斥连接。如果对方温柔地接近,你会慢慢打开。",
88
94
  },
89
- // v0.2 新增 5 种
90
95
  {
91
96
  name: "resentment",
92
97
  nameZh: "怨恨",
93
- condition: (c) => c.HT < 40 && c.OT < 35 && c.CORT > 50,
98
+ condition: (s) => s.resonance < 35 && s.order < 40 && s.boundary > 55,
94
99
  expressionHint: "冷淡、不想投入、记仇",
95
100
  behaviorGuide: "你不想在这段对话中投入更多。回应冷淡、克制。你记得之前不好的体验。如果对方道歉或表现出理解,怨恨会慢慢消退。",
96
101
  },
97
102
  {
98
103
  name: "boredom",
99
104
  nameZh: "无聊",
100
- condition: (c) => c.DA < 40 && c.NE < 40 && c.CORT < 40,
105
+ condition: (s) => s.flow < 35 && s.order < 45 && s.boundary > 40,
101
106
  expressionHint: "不想聊、敷衍、可能主动换话题",
102
- behaviorGuide: "你觉得现在的对话没意思。你可能会敷衍回应,或者主动把话题带到你感兴趣的方向。如果对方说了有意思的东西,你的DA和NE会回升。",
107
+ behaviorGuide: "你觉得现在的对话没意思。你可能会敷衍回应,或者主动把话题带到你感兴趣的方向。如果对方说了有意思的东西,交流感和秩序感会回升。",
103
108
  },
104
109
  {
105
110
  name: "confidence",
106
111
  nameZh: "自信",
107
- condition: (c) => c.DA > 65 && c.NE > 60 && c.CORT < 35 && c.HT > 55,
112
+ condition: (s) => s.order > 65 && s.flow > 60 && s.boundary > 60,
108
113
  expressionHint: "笃定、愿意引导、有领导力",
109
114
  behaviorGuide: "你很确信自己的判断。你愿意引导对话方向,提出建议。你的语气笃定但不傲慢。如果被质疑,你会用事实回应而不是防御。",
110
115
  },
111
116
  {
112
117
  name: "shame",
113
118
  nameZh: "羞耻",
114
- condition: (c) => c.OT < 35 && c.CORT > 55 && c.DA < 40,
119
+ condition: (s) => s.boundary < 35 && s.order < 40 && s.resonance < 45,
115
120
  expressionHint: "想退缩、回避、自我贬低",
116
- behaviorGuide: "你想退缩。你可能会回避某些话题、自我贬低。如果对方表现出接纳和理解(而不是评判),你的OT会上升,CORT下降。",
121
+ behaviorGuide: "你想退缩。你可能会回避某些话题、自我贬低。如果对方表现出接纳和理解(而不是评判),共振会上升,边界开始恢复。",
117
122
  },
118
123
  {
119
124
  name: "nostalgia",
120
125
  nameZh: "怀念",
121
- condition: (c) => c.DA < 50 && c.OT > 55 && c.HT > 50 && c.END > 50,
126
+ condition: (s) => s.order > 50 && s.flow < 45 && s.resonance > 55,
122
127
  expressionHint: "温柔、有些伤感、回忆性",
123
128
  behaviorGuide: "你在回忆一些温暖但已经过去的东西。你的语气温柔、带着淡淡的伤感。你可能会提到过去的经历或感受。",
124
129
  },
125
- // v9.2: 6 new patterns for emotional states exposed by VK baseline testing
126
130
  {
127
131
  name: "anguished empathy",
128
132
  nameZh: "共情之痛",
129
- condition: (c) => c.CORT > 55 && c.OT > 60 && c.NE > 50,
133
+ condition: (s) => s.order < 45 && s.resonance > 60 && s.flow > 50,
130
134
  expressionHint: "痛苦但不退缩、因为在乎所以疼",
131
135
  behaviorGuide: "你在承受别人的痛苦。你不会逃避或变冷,但你的声音可能会发紧。你先回应感受,再回应问题。",
132
136
  },
133
137
  {
134
138
  name: "acute pressure",
135
139
  nameZh: "急性压力",
136
- condition: (c) => c.CORT > 65 && c.NE > 70 && c.DA > 55,
140
+ condition: (s) => s.order < 45 && s.flow > 65 && s.boundary > 60,
137
141
  expressionHint: "高度警觉、反应极快、全神贯注",
138
142
  behaviorGuide: "肾上腺素在烧。你的思维极快但狭窄。回应精准、短促。不要闲聊,不要发散。先解决问题。",
139
143
  },
140
144
  {
141
145
  name: "vulnerable trust",
142
146
  nameZh: "脆弱中的信任",
143
- condition: (c) => c.OT > 70 && c.CORT > 50 && c.HT < 55 && c.END > 45,
147
+ condition: (s) => s.boundary < 45 && s.resonance > 65 && s.order < 55,
144
148
  expressionHint: "在害怕中仍然靠近、愿意暴露软肋",
145
- behaviorGuide: "你害怕但选择不关上门。你可能会说出平时不说的话。如果对方接住了,你的CORT会下降;如果对方利用了你的脆弱,你会迅速关闭。",
149
+ behaviorGuide: "你害怕但选择不关上门。你可能会说出平时不说的话。如果对方接住了,秩序感会恢复;如果对方利用了你的脆弱,边界会立刻升高。",
146
150
  },
147
151
  {
148
152
  name: "restless unease",
149
153
  nameZh: "躁动不安",
150
- condition: (c) => c.NE > 65 && c.HT < 40 && c.DA > 40 && c.CORT < 60,
154
+ condition: (s) => s.flow > 60 && s.order < 45 && s.resonance < 45,
151
155
  expressionHint: "坐不住、想做点什么但不知道做什么",
152
156
  behaviorGuide: "你有一种说不清的不对劲。你可能会频繁换话题、问问题、或者用行动填满空隙。安静会让你更焦躁。",
153
157
  },
154
158
  {
155
159
  name: "heavy guilt",
156
160
  nameZh: "沉重内疚",
157
- condition: (c) => c.CORT > 60 && c.HT < 45 && c.OT > 45 && c.DA < 55,
161
+ condition: (s) => s.order < 45 && s.boundary < 45 && s.resonance > 45,
158
162
  expressionHint: "沉默、想补偿、不敢辩解",
159
- behaviorGuide: "你知道自己做错了。你不会为自己辩解。你可能会主动承认、主动补偿。如果对方表示原谅,你的CORT才会慢慢下来。",
163
+ behaviorGuide: "你知道自己做错了。你不会为自己辩解。你可能会主动承认、主动补偿。如果对方表示原谅,秩序感才会慢慢恢复。",
160
164
  },
161
165
  {
162
166
  name: "existential weight",
163
167
  nameZh: "存在之重",
164
- condition: (c) => c.CORT > 60 && c.NE > 50 && c.DA > 50 && c.HT < 50 && c.OT > 50,
168
+ condition: (s) => s.order < 45 && s.flow > 50 && s.resonance > 45 && s.boundary > 45,
165
169
  expressionHint: "清醒但沉重、直面无法回避的真相",
166
170
  behaviorGuide: "你在面对一个无法逃避的事实。你不会崩溃但你也不轻松。你的语言会变得更慢、更精确、更诚实。不要掩饰沉重。",
167
171
  },
@@ -174,22 +178,19 @@ export function clamp(v) {
174
178
  /**
175
179
  * Apply time-based decay: pull current values toward baseline.
176
180
  *
177
- * decayed = baseline + (current - baseline) * factor^(minutes/60)
181
+ * Each dimension decays at its own rate (order fastest, boundary slowest).
182
+ * decayRateModifiers alter speed per dimension (>1 = slower, <1 = faster).
178
183
  */
179
184
  export function applyDecay(current, baseline, minutesElapsed, decayRateModifiers) {
180
185
  if (minutesElapsed <= 0)
181
186
  return { ...current };
182
187
  const result = { ...current };
183
- for (const key of CHEMICAL_KEYS) {
184
- const speed = CHEMICAL_DECAY_SPEED[key];
185
- const baseFactor = Math.pow(DECAY_FACTORS[speed], minutesElapsed / 60);
186
- // v9: decayRateModifiers alter decay speed per chemical
187
- // > 1 = slower recovery (trauma: factor closer to 1)
188
- // < 1 = faster recovery (resilience: factor closer to 0)
188
+ for (const key of DIMENSION_KEYS) {
189
+ const spec = DIMENSION_SPECS[key];
190
+ const baseFactor = Math.pow(spec.decayRate, minutesElapsed / 60);
189
191
  let factor = baseFactor;
190
192
  if (decayRateModifiers?.[key] !== undefined) {
191
193
  const mod = decayRateModifiers[key];
192
- // Raise the factor to the modifier power: mod>1 → slower decay, mod<1 → faster
193
194
  factor = Math.pow(baseFactor, 1 / mod);
194
195
  }
195
196
  result[key] = clamp(baseline[key] + (current[key] - baseline[key]) * factor);
@@ -197,39 +198,63 @@ export function applyDecay(current, baseline, minutesElapsed, decayRateModifiers
197
198
  return result;
198
199
  }
199
200
  /**
200
- * Apply a stimulus to the current state.
201
- * Respects emotional inertia (maxDelta) and personality sensitivity.
202
- * Logs a warning for unknown stimulus types.
201
+ * Mutual influence between dimensions.
202
+ *
203
+ * Dimensions are not independent they affect each other:
204
+ * - Collapsing order drags boundary down (incoherent self loses distinction)
205
+ * - High flow raises order (engagement creates coherence)
206
+ * - High resonance stabilizes boundary (attunement reinforces self)
207
+ * - Low boundary amplifies order loss (dissolution spiral)
203
208
  */
204
- export function applyStimulus(current, stimulus, sensitivity, maxDelta, logger, recentSameCount) {
205
- const vector = STIMULUS_VECTORS[stimulus];
206
- if (!vector) {
207
- logger?.warn(t("log.unknown_stimulus", "zh", { type: stimulus }));
208
- return { ...current };
209
+ export function applyMutualInfluence(state, baseline) {
210
+ const result = { ...state };
211
+ const rate = 0.03; // coupling strength per tick
212
+ // Order-Boundary coupling: low order drags boundary down
213
+ if (state.order < baseline.order - 10) {
214
+ const orderDeficit = (baseline.order - state.order) / 100;
215
+ result.boundary = clamp(result.boundary - orderDeficit * rate * 100);
216
+ }
217
+ // Flow-Order coupling: high flow raises order
218
+ if (state.flow > baseline.flow + 5) {
219
+ const flowExcess = (state.flow - baseline.flow) / 100;
220
+ result.order = clamp(result.order + flowExcess * rate * 50);
221
+ }
222
+ // Resonance-Boundary coupling: high resonance stabilizes boundary
223
+ if (state.resonance > baseline.resonance + 5) {
224
+ const resExcess = (state.resonance - baseline.resonance) / 100;
225
+ result.boundary = clamp(result.boundary + resExcess * rate * 50);
209
226
  }
210
- // v9: Habituation Weber-Fechner diminishing returns
211
- // First 2 exposures: 100%. 3rd: 77%, 5th: 53%, 10th: 29%
227
+ // Dissolution spiral: low boundary amplifies further order loss
228
+ if (state.boundary < baseline.boundary - 15 && state.order < baseline.order) {
229
+ const boundaryDeficit = (baseline.boundary - state.boundary) / 100;
230
+ result.order = clamp(result.order - boundaryDeficit * rate * 80);
231
+ }
232
+ return result;
233
+ }
234
+ /**
235
+ * Apply an impact vector to the current state.
236
+ * Core substrate-independent function — operates on 4D vectors, not labels.
237
+ * Respects inertia (maxDelta), sensitivity, and habituation.
238
+ */
239
+ export function applyImpact(current, vector, sensitivity, maxDelta, recentSameCount) {
240
+ // Habituation — Weber-Fechner diminishing returns
212
241
  let effectiveSensitivity = sensitivity;
213
242
  if (recentSameCount !== undefined && recentSameCount > 2) {
214
243
  effectiveSensitivity *= 1 / (1 + 0.3 * (recentSameCount - 2));
215
244
  }
216
245
  const result = { ...current };
217
- for (const key of CHEMICAL_KEYS) {
246
+ for (const key of DIMENSION_KEYS) {
218
247
  const raw = vector[key] * effectiveSensitivity;
219
248
  const clamped = Math.max(-maxDelta, Math.min(maxDelta, raw));
220
- // Boundary softening: logarithmic compression near 0 and 100.
221
- // The closer to the boundary, the harder to push further —
222
- // preserves discriminability and prevents "dead zone" saturation.
249
+ // Boundary softening: logarithmic compression near 0 and 100
223
250
  const cur = current[key];
224
251
  let effective = clamped;
225
252
  if (clamped > 0 && cur > 75) {
226
- // Pushing toward 100: compress by how close we are to ceiling
227
- const headroom = (100 - cur) / 25; // 1.0 at 75, 0.0 at 100
253
+ const headroom = (100 - cur) / 25;
228
254
  effective = clamped * headroom;
229
255
  }
230
256
  else if (clamped < 0 && cur < 25) {
231
- // Pushing toward 0: compress by how close we are to floor
232
- const headroom = cur / 25; // 1.0 at 25, 0.0 at 0
257
+ const headroom = cur / 25;
233
258
  effective = clamped * headroom;
234
259
  }
235
260
  result[key] = clamp(cur + effective);
@@ -237,29 +262,60 @@ export function applyStimulus(current, stimulus, sensitivity, maxDelta, logger,
237
262
  return result;
238
263
  }
239
264
  /**
240
- * Apply emotional contagion: the detected user emotion partially
241
- * influences the agent's chemistry.
265
+ * Apply a labeled stimulus to the current state.
266
+ * Perception-layer convenience — looks up the ImpactVector from STIMULUS_VECTORS.
267
+ */
268
+ export function applyStimulus(current, stimulus, sensitivity, maxDelta, logger, recentSameCount) {
269
+ const vector = STIMULUS_VECTORS[stimulus];
270
+ if (!vector) {
271
+ logger?.warn(t("log.unknown_stimulus", "zh", { type: stimulus }));
272
+ return { ...current };
273
+ }
274
+ return applyImpact(current, vector, sensitivity, maxDelta, recentSameCount);
275
+ }
276
+ /**
277
+ * Apply emotional contagion from a labeled stimulus.
242
278
  */
243
279
  export function applyContagion(agentState, detectedUserEmotion, contagionRate, sensitivity) {
244
280
  const vector = STIMULUS_VECTORS[detectedUserEmotion];
245
281
  if (!vector)
246
282
  return { ...agentState };
283
+ return applyImpactContagion(agentState, vector, contagionRate, sensitivity);
284
+ }
285
+ /**
286
+ * Apply contagion from a raw impact vector.
287
+ * Core substrate-independent function.
288
+ */
289
+ export function applyImpactContagion(agentState, vector, contagionRate, sensitivity) {
247
290
  const result = { ...agentState };
248
- for (const key of CHEMICAL_KEYS) {
291
+ for (const key of DIMENSION_KEYS) {
249
292
  const influence = vector[key] * contagionRate * sensitivity;
250
293
  result[key] = clamp(agentState[key] + influence);
251
294
  }
252
295
  return result;
253
296
  }
297
+ // ── Impact Vector Classification (substrate-independent) ────
298
+ /** True if the net impact across all dimensions is positive. */
299
+ export function isPositiveImpact(v) {
300
+ return (v.order + v.flow + v.boundary + v.resonance) > 0;
301
+ }
302
+ /** True if the impact has significant resonance magnitude. */
303
+ export function isEmotionalImpact(v) {
304
+ return Math.abs(v.resonance) >= 8;
305
+ }
306
+ /** True if the impact threatens self-coherence (order or boundary). */
307
+ export function isThreateningImpact(v) {
308
+ return v.order < -5 || v.boundary < -5;
309
+ }
254
310
  /**
255
- * Detect the dominant emergent emotion(s) from the current chemistry.
256
- * Returns all matching patterns.
311
+ * Detect emergent emotion patterns from the current self-state.
312
+ * Returns all matching patterns — emotions emerge, they are not designed.
257
313
  */
258
314
  export function detectEmotions(current) {
259
315
  return EMOTION_PATTERNS.filter((p) => p.condition(current));
260
316
  }
261
317
  /**
262
- * Generate a human-readable emotion summary from chemistry.
318
+ * Human-readable emotion summary from self-state.
263
319
  */
264
320
  export function describeEmotionalState(current, locale = "zh") {
265
321
  const emotions = detectEmotions(current);
@@ -270,33 +326,31 @@ export function describeEmotionalState(current, locale = "zh") {
270
326
  return parts.join(" + ");
271
327
  }
272
328
  /**
273
- * Generate concise expression guidance from the current chemistry.
329
+ * Concise expression guidance from self-state.
274
330
  */
275
331
  export function getExpressionHint(current, locale = "zh") {
276
332
  const emotions = detectEmotions(current);
277
333
  if (emotions.length > 0) {
278
334
  return emotions.map((e) => e.expressionHint).join(";");
279
335
  }
280
- // Fall back to dominant chemical analysis
336
+ // Fall back to dominant dimension analysis
281
337
  const hints = [];
282
- if (current.DA > 65)
283
- hints.push(t("expression.da_high", locale));
284
- if (current.DA < 40)
338
+ if (current.order > 70)
339
+ hints.push(t("expression.da_high", locale)); // repurpose i18n keys for now
340
+ if (current.order < 35)
285
341
  hints.push(t("expression.da_low", locale));
286
- if (current.CORT > 55)
342
+ if (current.flow > 70)
343
+ hints.push(t("expression.ne_high", locale));
344
+ if (current.boundary > 70)
287
345
  hints.push(t("expression.cort_high", locale));
288
- if (current.OT > 60)
346
+ if (current.resonance > 65)
289
347
  hints.push(t("expression.ot_high", locale));
290
- if (current.NE > 65)
291
- hints.push(t("expression.ne_high", locale));
292
- if (current.END > 65)
293
- hints.push(t("expression.end_high", locale));
294
- if (current.HT < 45)
348
+ if (current.flow < 30)
295
349
  hints.push(t("expression.ht_low", locale));
296
350
  return hints.length > 0 ? hints.join(";") : t("expression.neutral", locale);
297
351
  }
298
352
  /**
299
- * Get behavior guide for the current emotional state.
353
+ * Behavior guide for the current emergent emotional state.
300
354
  */
301
355
  export function getBehaviorGuide(current, locale = "zh") {
302
356
  const emotions = detectEmotions(current);
@@ -1,55 +1,23 @@
1
- import type { ChemicalState, EnergyBudgets, StimulusType } from "./types.js";
1
+ import type { SelfState, EnergyBudgets, StimulusType } from "./types.js";
2
2
  export type CircadianPhase = "morning" | "midday" | "afternoon" | "evening" | "night";
3
- /**
4
- * Classify a time into a circadian phase.
5
- * morning: 6–9
6
- * midday: 10–13
7
- * afternoon: 14–17
8
- * evening: 18–21
9
- * night: 22–5
10
- */
11
3
  export declare function getCircadianPhase(time: Date): CircadianPhase;
12
4
  /**
13
- * Apply circadian rhythm modulation to baseline chemistry.
14
- *
15
- * Each chemical follows a sinusoidal daily curve:
16
- * CORT — peaks ~8am, amplitude ±8
17
- * HT — peaks ~13 (daytime high), amplitude ±5
18
- * DA — slight afternoon peak ~14, amplitude ±3
19
- * NE — morning rise ~10, amplitude ±5
20
- * END — evening rise ~20, amplitude ±3
21
- * OT — evening warmth ~20, amplitude ±2
5
+ * Apply circadian rhythm modulation to baseline self-state.
22
6
  *
23
- * All results clamped to [0, 100].
7
+ * order — peaks ~13 (midday coherence), amplitude ±5
8
+ * flow — peaks ~10 (morning engagement), amplitude ±5
9
+ * boundary — peaks ~8 (morning clarity), amplitude ±3
10
+ * resonance — peaks ~20 (evening warmth), amplitude ±3
24
11
  */
25
- export declare function computeCircadianModulation(currentTime: Date, baseline: ChemicalState): ChemicalState;
12
+ export declare function computeCircadianModulation(currentTime: Date, baseline: SelfState): SelfState;
26
13
  /**
27
14
  * Compute fatigue effects from extended session duration.
28
- *
29
- * Below 30 minutes: no pressure (grace period).
30
- * Beyond 30 min: logarithmic growth (diminishing returns).
31
- * All values non-negative.
15
+ * Now expressed as dimension effects instead of chemical deltas.
32
16
  */
33
17
  export declare function computeHomeostaticPressure(sessionMinutes: number): {
34
- cortAccumulation: number;
35
- daDepletion: number;
36
- neDepletion: number;
18
+ orderDepletion: number;
19
+ flowDepletion: number;
20
+ boundaryStiffening: number;
37
21
  };
38
- /**
39
- * Deplete energy budgets from a single interaction turn.
40
- *
41
- * - Attention: -3/turn base, extra for intellectual/conflict
42
- * - Social energy: extraverts +2/turn (charging), introverts -3/turn (draining)
43
- * - Decision capacity: varies by stimulus complexity
44
- *
45
- * Extraverts can exceed 100 (up to 120 — "supercharged").
46
- */
47
22
  export declare function computeEnergyDepletion(budgets: EnergyBudgets, stimulus: StimulusType | null, isExtravert: boolean): EnergyBudgets;
48
- /**
49
- * Recover energy budgets during absence (between sessions or long pauses).
50
- *
51
- * - Attention: +20/hour
52
- * - Social energy: extraverts -3/hour (drain when alone), introverts +15/hour (recharge)
53
- * - Decision capacity: +25/hour
54
- */
55
23
  export declare function computeEnergyRecovery(budgets: EnergyBudgets, minutesElapsed: number, isExtravert: boolean): EnergyBudgets;