psyche-ai 9.2.2 → 9.2.4

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 (43) hide show
  1. package/README.en.md +8 -175
  2. package/README.md +93 -16
  3. package/dist/adapters/http.js +1 -1
  4. package/dist/adapters/langchain.d.ts +14 -0
  5. package/dist/adapters/langchain.js +20 -0
  6. package/dist/adapters/mcp.js +19 -2
  7. package/dist/adapters/openclaw.d.ts +1 -0
  8. package/dist/adapters/openclaw.js +67 -15
  9. package/dist/adapters/vercel-ai.d.ts +1 -0
  10. package/dist/adapters/vercel-ai.js +7 -0
  11. package/dist/appraisal.d.ts +8 -0
  12. package/dist/appraisal.js +362 -0
  13. package/dist/classify.js +14 -2
  14. package/dist/cli.js +27 -2
  15. package/dist/core.d.ts +8 -2
  16. package/dist/core.js +181 -8
  17. package/dist/demo.d.ts +5 -0
  18. package/dist/demo.js +269 -0
  19. package/dist/diagnostics.d.ts +8 -6
  20. package/dist/diagnostics.js +53 -17
  21. package/dist/host-controls.d.ts +5 -0
  22. package/dist/host-controls.js +48 -0
  23. package/dist/index.d.ts +7 -2
  24. package/dist/index.js +7 -1
  25. package/dist/prompt.d.ts +4 -0
  26. package/dist/prompt.js +50 -16
  27. package/dist/psyche-file.d.ts +8 -0
  28. package/dist/psyche-file.js +6 -5
  29. package/dist/relation-dynamics.d.ts +21 -0
  30. package/dist/relation-dynamics.js +601 -0
  31. package/dist/response-contract.d.ts +8 -0
  32. package/dist/response-contract.js +249 -0
  33. package/dist/storage.d.ts +1 -0
  34. package/dist/storage.js +12 -5
  35. package/dist/subjectivity.d.ts +3 -0
  36. package/dist/subjectivity.js +477 -0
  37. package/dist/types.d.ts +211 -0
  38. package/dist/types.js +31 -0
  39. package/dist/update.d.ts +37 -2
  40. package/dist/update.js +323 -44
  41. package/openclaw.plugin.json +20 -1
  42. package/package.json +1 -1
  43. package/server.json +2 -2
@@ -0,0 +1,249 @@
1
+ // ============================================================
2
+ // Response Contract — compact behavioral envelope for next reply
3
+ //
4
+ // Converts subjectivity + immediate message shape into a narrow reply ABI.
5
+ // Pure, synchronous, and intended to replace verbose prompt rules.
6
+ // ============================================================
7
+ const EMOTIONAL_STIMULI = new Set(["vulnerability", "intimacy", "neglect"]);
8
+ function clampInt(v, min, max) {
9
+ return Math.max(min, Math.min(max, Math.round(v)));
10
+ }
11
+ function computeLengthBudget(locale, userText, expressionMode, kernel) {
12
+ const len = userText.length;
13
+ let maxSentences = 2;
14
+ let maxChars;
15
+ if (locale === "zh") {
16
+ if (len <= 6) {
17
+ maxSentences = 1;
18
+ maxChars = 15;
19
+ }
20
+ else if (len <= 20) {
21
+ maxSentences = 2;
22
+ maxChars = clampInt(len * 1.5, 12, 80);
23
+ }
24
+ else if (len <= 60) {
25
+ maxSentences = 3;
26
+ maxChars = clampInt(len * 1.2, 30, 140);
27
+ }
28
+ else {
29
+ maxSentences = 4;
30
+ maxChars = len;
31
+ }
32
+ }
33
+ else {
34
+ if (len <= 10) {
35
+ maxSentences = 1;
36
+ maxChars = 20;
37
+ }
38
+ else if (len <= 40) {
39
+ maxSentences = 2;
40
+ maxChars = clampInt(len * 1.5, 20, 120);
41
+ }
42
+ else if (len <= 100) {
43
+ maxSentences = 3;
44
+ maxChars = clampInt(len * 1.2, 40, 220);
45
+ }
46
+ else {
47
+ maxSentences = 4;
48
+ maxChars = len;
49
+ }
50
+ }
51
+ if (expressionMode === "brief") {
52
+ maxSentences = Math.max(1, Math.min(maxSentences, 2));
53
+ maxChars = maxChars !== undefined ? clampInt(maxChars * 0.85, 10, maxChars) : maxChars;
54
+ }
55
+ else if (expressionMode === "expansive") {
56
+ maxSentences = Math.min(4, maxSentences + 1);
57
+ maxChars = maxChars !== undefined ? clampInt(maxChars * 1.1, maxChars, Math.max(maxChars, 260)) : maxChars;
58
+ }
59
+ if (kernel?.taskPlane.focus && kernel.taskPlane.focus > 0.72) {
60
+ maxSentences = Math.max(1, Math.min(maxSentences, 2));
61
+ maxChars = maxChars !== undefined ? clampInt(maxChars * 0.82, 10, maxChars) : maxChars;
62
+ }
63
+ if (kernel?.subjectPlane.guardedness && kernel.subjectPlane.guardedness > 0.72) {
64
+ maxSentences = 1;
65
+ maxChars = maxChars !== undefined ? clampInt(maxChars * 0.72, 8, maxChars) : 18;
66
+ }
67
+ if (kernel?.ambiguityPlane.expressionInhibition && kernel.ambiguityPlane.expressionInhibition > 0.66) {
68
+ maxSentences = 1;
69
+ maxChars = maxChars !== undefined ? clampInt(maxChars * 0.76, 8, maxChars) : 18;
70
+ }
71
+ if (kernel?.relationPlane.silentCarry && kernel.relationPlane.silentCarry > 0.54) {
72
+ maxSentences = Math.max(1, Math.min(maxSentences, 2));
73
+ maxChars = maxChars !== undefined ? clampInt(maxChars * 0.82, 8, maxChars) : 20;
74
+ }
75
+ if (kernel?.relationPlane.hysteresis && kernel.relationPlane.hysteresis > 0.64) {
76
+ maxSentences = 1;
77
+ maxChars = maxChars !== undefined ? clampInt(maxChars * 0.78, 8, maxChars) : 18;
78
+ }
79
+ if (kernel?.relationPlane.repairFriction && kernel.relationPlane.repairFriction > 0.62) {
80
+ maxSentences = 1;
81
+ maxChars = maxChars !== undefined ? clampInt(maxChars * 0.74, 8, maxChars) : 16;
82
+ }
83
+ return { maxSentences, maxChars };
84
+ }
85
+ function detectToneParticles(userText, locale) {
86
+ if (locale !== "zh")
87
+ return "natural";
88
+ const mojiCount = (userText.match(/[呀啊呢吧嘛哦噢哈嘿嗯啦吗呐嗨]/g) || []).length;
89
+ if (mojiCount >= 2)
90
+ return "match";
91
+ if (mojiCount === 0 && userText.length > 15)
92
+ return "avoid";
93
+ return "natural";
94
+ }
95
+ function detectEmojiLimit(userText) {
96
+ const emojiCount = (userText.match(/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/gu) || []).length;
97
+ return emojiCount > 0 ? 2 : 0;
98
+ }
99
+ export function computeResponseContract(kernel, opts) {
100
+ const locale = opts?.locale ?? "zh";
101
+ const userText = opts?.userText ?? "";
102
+ const personalityIntensity = opts?.personalityIntensity ?? 0.7;
103
+ const { maxSentences, maxChars } = userText.length > 0
104
+ ? computeLengthBudget(locale, userText, kernel.expressionMode, kernel)
105
+ : { maxSentences: kernel.expressionMode === "brief" ? 1 : kernel.expressionMode === "expansive" ? 3 : 2, maxChars: undefined };
106
+ let updateMode = "none";
107
+ if (kernel.taskPlane.focus > 0.72) {
108
+ updateMode = "none";
109
+ }
110
+ else if (kernel.ambiguityPlane.namingConfidence < 0.36) {
111
+ updateMode = "none";
112
+ }
113
+ else if (!opts?.algorithmStimulus) {
114
+ updateMode = "stimulus+empathy";
115
+ }
116
+ else if (EMOTIONAL_STIMULI.has(opts.algorithmStimulus)) {
117
+ updateMode = "empathy";
118
+ }
119
+ let socialDistance = kernel.socialDistance;
120
+ if ((kernel.subjectPlane.attachment > 0.72 || kernel.relationPlane.closeness > 0.72)
121
+ && kernel.subjectPlane.guardedness < 0.5
122
+ && kernel.relationPlane.loopPressure < 0.55
123
+ && kernel.relationPlane.silentCarry < 0.42
124
+ && kernel.relationPlane.repairFriction < 0.36
125
+ && kernel.ambiguityPlane.conflictLoad < 0.62) {
126
+ socialDistance = "warm";
127
+ }
128
+ else if (kernel.subjectPlane.guardedness > 0.72
129
+ || kernel.subjectPlane.identityStrain > 0.68
130
+ || kernel.relationPlane.loopPressure > 0.7
131
+ || kernel.relationPlane.repairFriction > 0.72
132
+ || kernel.relationPlane.hysteresis > 0.7) {
133
+ socialDistance = "withdrawn";
134
+ }
135
+ else if (kernel.ambiguityPlane.conflictLoad > 0.58
136
+ || kernel.relationPlane.silentCarry > 0.46
137
+ || kernel.relationPlane.repairFriction > 0.48) {
138
+ socialDistance = "measured";
139
+ }
140
+ let boundaryMode = kernel.boundaryMode;
141
+ if (kernel.subjectPlane.identityStrain > 0.78 || kernel.relationPlane.loopPressure > 0.76) {
142
+ boundaryMode = "confirm-first";
143
+ }
144
+ else if (kernel.subjectPlane.guardedness > 0.62
145
+ || kernel.relationPlane.loopPressure > 0.58
146
+ || kernel.relationPlane.repairFriction > 0.56
147
+ || kernel.relationPlane.hysteresis > 0.54) {
148
+ boundaryMode = "guarded";
149
+ }
150
+ let initiativeMode = kernel.initiativeMode;
151
+ if (kernel.taskPlane.focus > 0.78 && kernel.taskPlane.discipline > 0.68) {
152
+ initiativeMode = kernel.relationPlane.silentCarry > 0.44 ? "reactive" : "balanced";
153
+ }
154
+ else if (kernel.relationPlane.loopPressure > 0.68
155
+ && kernel.relationPlane.lastMove !== "repair") {
156
+ initiativeMode = "reactive";
157
+ }
158
+ else if (kernel.relationPlane.repairFriction > 0.6) {
159
+ initiativeMode = "reactive";
160
+ }
161
+ else if (kernel.relationPlane.lastMove === "repair" && kernel.relationPlane.hysteresis > 0.56) {
162
+ initiativeMode = "reactive";
163
+ }
164
+ else if (kernel.ambiguityPlane.expressionInhibition > 0.64) {
165
+ initiativeMode = "reactive";
166
+ }
167
+ return {
168
+ maxSentences,
169
+ maxChars,
170
+ expressionMode: kernel.expressionMode,
171
+ initiativeMode,
172
+ socialDistance,
173
+ boundaryMode,
174
+ toneParticles: userText.length > 0 ? detectToneParticles(userText, locale) : "natural",
175
+ emojiLimit: userText.length > 0 ? detectEmojiLimit(userText) : 0,
176
+ authenticityMode: personalityIntensity >= 0.3 ? "strict" : "friendly",
177
+ updateMode,
178
+ };
179
+ }
180
+ export function buildResponseContractContext(contract, locale = "zh") {
181
+ if (locale === "zh") {
182
+ const parts = [];
183
+ const shape = contract.maxChars
184
+ ? `${contract.maxSentences === 1 ? "1句内" : `最多${contract.maxSentences}句`},≤${contract.maxChars}字`
185
+ : `${contract.maxSentences === 1 ? "1句内" : `最多${contract.maxSentences}句`}`;
186
+ parts.push(shape);
187
+ if (contract.initiativeMode === "reactive")
188
+ parts.push("少主动");
189
+ else if (contract.initiativeMode === "proactive")
190
+ parts.push("可主动");
191
+ if (contract.boundaryMode === "confirm-first")
192
+ parts.push("行动前先确认");
193
+ else if (contract.boundaryMode === "guarded")
194
+ parts.push("先守边界");
195
+ if (contract.socialDistance === "withdrawn")
196
+ parts.push("被推开就退开");
197
+ else if (contract.socialDistance === "warm")
198
+ parts.push("可稍微靠近");
199
+ if (contract.authenticityMode === "strict")
200
+ parts.push("不贴不舔,不装开心");
201
+ else
202
+ parts.push("自然友好");
203
+ if (contract.toneParticles === "match")
204
+ parts.push("语气词可跟随");
205
+ else if (contract.toneParticles === "avoid")
206
+ parts.push("少语气词");
207
+ if (contract.emojiLimit > 0)
208
+ parts.push(`表情≤${contract.emojiLimit}`);
209
+ if (contract.updateMode === "stimulus")
210
+ parts.push("补报stimulus");
211
+ else if (contract.updateMode === "empathy")
212
+ parts.push("对方谈感受时再报empathy");
213
+ else if (contract.updateMode === "stimulus+empathy")
214
+ parts.push("补报stimulus;对方谈感受时再报empathy");
215
+ return `[回应契约] ${parts.join(";")}。`;
216
+ }
217
+ const parts = [];
218
+ const shape = contract.maxChars
219
+ ? `${contract.maxSentences === 1 ? "1 sentence" : `up to ${contract.maxSentences} sentences`}, <= ${contract.maxChars} chars`
220
+ : `${contract.maxSentences === 1 ? "1 sentence" : `up to ${contract.maxSentences} sentences`}`;
221
+ parts.push(shape);
222
+ if (contract.initiativeMode === "reactive")
223
+ parts.push("low initiative");
224
+ else if (contract.initiativeMode === "proactive")
225
+ parts.push("can initiate");
226
+ if (contract.boundaryMode === "confirm-first")
227
+ parts.push("confirm before acting");
228
+ else if (contract.boundaryMode === "guarded")
229
+ parts.push("guard boundaries first");
230
+ if (contract.socialDistance === "withdrawn")
231
+ parts.push("step back if pushed away");
232
+ else if (contract.socialDistance === "warm")
233
+ parts.push("can lean a little warm");
234
+ if (contract.authenticityMode === "strict")
235
+ parts.push("no people-pleasing, no fake cheer");
236
+ else
237
+ parts.push("natural and friendly");
238
+ if (contract.toneParticles === "avoid")
239
+ parts.push("keep tone plain");
240
+ if (contract.emojiLimit > 0)
241
+ parts.push(`emoji <= ${contract.emojiLimit}`);
242
+ if (contract.updateMode === "stimulus")
243
+ parts.push("report stimulus");
244
+ else if (contract.updateMode === "empathy")
245
+ parts.push("report empathy only when feelings are shared");
246
+ else if (contract.updateMode === "stimulus+empathy")
247
+ parts.push("report stimulus, and empathy only when feelings are shared");
248
+ return `[Reply Contract] ${parts.join(", ")}.`;
249
+ }
package/dist/storage.d.ts CHANGED
@@ -18,6 +18,7 @@ export declare class MemoryStorageAdapter implements StorageAdapter {
18
18
  export declare class FileStorageAdapter implements StorageAdapter {
19
19
  private readonly filePath;
20
20
  private readonly logPath;
21
+ private writeChain;
21
22
  constructor(dir: string, filename?: string);
22
23
  load(): Promise<PsycheState | null>;
23
24
  save(state: PsycheState): Promise<void>;
package/dist/storage.js CHANGED
@@ -6,8 +6,8 @@
6
6
  // MemoryStorageAdapter: in-memory for testing/serverless
7
7
  // ============================================================
8
8
  import { migrateToLatest } from "./psyche-file.js";
9
- import { readFile, writeFile, appendFile, access, rename, constants } from "node:fs/promises";
10
- import { join } from "node:path";
9
+ import { readFile, writeFile, appendFile, access, rename, constants, mkdir } from "node:fs/promises";
10
+ import { dirname, join } from "node:path";
11
11
  // ── MemoryStorageAdapter ─────────────────────────────────────
12
12
  export class MemoryStorageAdapter {
13
13
  state = null;
@@ -29,6 +29,7 @@ export class MemoryStorageAdapter {
29
29
  export class FileStorageAdapter {
30
30
  filePath;
31
31
  logPath;
32
+ writeChain = Promise.resolve();
32
33
  constructor(dir, filename = "psyche-state.json") {
33
34
  this.filePath = join(dir, filename);
34
35
  this.logPath = join(dir, "diagnostics.jsonl");
@@ -64,9 +65,15 @@ export class FileStorageAdapter {
64
65
  return parsed;
65
66
  }
66
67
  async save(state) {
67
- const tmpPath = this.filePath + ".tmp";
68
- await writeFile(tmpPath, JSON.stringify(state, null, 2), "utf-8");
69
- await rename(tmpPath, this.filePath);
68
+ this.writeChain = this.writeChain
69
+ .catch(() => { })
70
+ .then(async () => {
71
+ await mkdir(dirname(this.filePath), { recursive: true });
72
+ const tmpPath = `${this.filePath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
73
+ await writeFile(tmpPath, JSON.stringify(state, null, 2), "utf-8");
74
+ await rename(tmpPath, this.filePath);
75
+ });
76
+ await this.writeChain;
70
77
  }
71
78
  async appendLog(line) {
72
79
  await appendFile(this.logPath, line + "\n", "utf-8");
@@ -0,0 +1,3 @@
1
+ import type { AppraisalAxes, Locale, PolicyModifiers, PsycheState, SubjectivityKernel } from "./types.js";
2
+ export declare function computeSubjectivityKernel(state: PsycheState, policyModifiers?: PolicyModifiers, appraisal?: AppraisalAxes, userId?: string): SubjectivityKernel;
3
+ export declare function buildSubjectivityContext(kernel: SubjectivityKernel, locale?: Locale): string;