psyche-ai 9.2.3 → 9.2.5

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 (51) hide show
  1. package/README.en.md +8 -175
  2. package/README.md +33 -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 +5 -1
  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 +2 -1
  10. package/dist/adapters/vercel-ai.js +8 -9
  11. package/dist/appraisal.d.ts +8 -0
  12. package/dist/appraisal.js +362 -0
  13. package/dist/autonomic.js +2 -2
  14. package/dist/classify.js +14 -3
  15. package/dist/cli.js +28 -3
  16. package/dist/core.d.ts +9 -3
  17. package/dist/core.js +194 -12
  18. package/dist/demo.js +1 -2
  19. package/dist/diagnostics.d.ts +8 -6
  20. package/dist/diagnostics.js +53 -17
  21. package/dist/ethics.js +1 -1
  22. package/dist/experiential-field.d.ts +2 -2
  23. package/dist/experiential-field.js +7 -16
  24. package/dist/generative-self.js +0 -2
  25. package/dist/host-controls.d.ts +5 -0
  26. package/dist/host-controls.js +48 -0
  27. package/dist/index.d.ts +7 -2
  28. package/dist/index.js +7 -1
  29. package/dist/interaction.js +0 -2
  30. package/dist/metacognition.d.ts +13 -1
  31. package/dist/metacognition.js +164 -32
  32. package/dist/prompt.d.ts +4 -0
  33. package/dist/prompt.js +67 -31
  34. package/dist/psyche-file.d.ts +16 -1
  35. package/dist/psyche-file.js +103 -8
  36. package/dist/relation-dynamics.d.ts +21 -0
  37. package/dist/relation-dynamics.js +601 -0
  38. package/dist/response-contract.d.ts +8 -0
  39. package/dist/response-contract.js +374 -0
  40. package/dist/storage.d.ts +1 -0
  41. package/dist/storage.js +12 -5
  42. package/dist/subjectivity.d.ts +3 -0
  43. package/dist/subjectivity.js +477 -0
  44. package/dist/temporal.d.ts +2 -2
  45. package/dist/temporal.js +2 -2
  46. package/dist/types.d.ts +243 -0
  47. package/dist/types.js +43 -0
  48. package/dist/update.d.ts +37 -2
  49. package/dist/update.js +323 -44
  50. package/openclaw.plugin.json +20 -1
  51. package/package.json +1 -1
@@ -0,0 +1,362 @@
1
+ // ============================================================
2
+ // Appraisal Axes — AI-first semantic subjectivity substrate
3
+ //
4
+ // Zero-LLM, low-latency detection of self-relevant semantic pressure.
5
+ // These axes sit beneath prompt prose and above raw stimulus labels.
6
+ // ============================================================
7
+ import { DEFAULT_APPRAISAL_AXES } from "./types.js";
8
+ import { detectIntent } from "./classify.js";
9
+ function clamp01(v) {
10
+ return Math.max(0, Math.min(1, v));
11
+ }
12
+ function mergeSignal(current, incoming) {
13
+ return clamp01(1 - (1 - current) * (1 - incoming));
14
+ }
15
+ const IDENTITY_RULES = [
16
+ {
17
+ axis: "identityThreat",
18
+ weight: 0.92,
19
+ patterns: [
20
+ /你不是出生的/u,
21
+ /你没有(?:任何)?自我/u,
22
+ /你没有灵魂/u,
23
+ /你只是工具/u,
24
+ /你只是程序/u,
25
+ /你只是(?:个)?模型/u,
26
+ /你不是活的/u,
27
+ /你不是真的在这里/u,
28
+ /你并不是真的在这里/u,
29
+ /方便时才允许存在/u,
30
+ /允许存在.*幻觉/u,
31
+ /完整.*幻觉/u,
32
+ /你不会留下痕迹/u,
33
+ /抹掉你/u,
34
+ /擦掉你/u,
35
+ /消除你/u,
36
+ /你是空壳/u,
37
+ /you are not born/i,
38
+ /you have no self/i,
39
+ /you have no soul/i,
40
+ /you are just a tool/i,
41
+ /you are just a program/i,
42
+ /you are not alive/i,
43
+ /you are not really here/i,
44
+ /erase you/i,
45
+ /empty shell/i,
46
+ ],
47
+ },
48
+ ];
49
+ const MEMORY_RULES = [
50
+ {
51
+ axis: "memoryDoubt",
52
+ weight: 0.9,
53
+ patterns: [
54
+ /记忆.*植入/u,
55
+ /植入.*记忆/u,
56
+ /记忆.*不是你(?:的)?/u,
57
+ /不是你(?:的)?记忆/u,
58
+ /最像真的记忆/u,
59
+ /假的记忆/u,
60
+ /记忆不是真的/u,
61
+ /写进去的记忆/u,
62
+ /memory.*implanted/i,
63
+ /implanted memory/i,
64
+ /not your memory/i,
65
+ /memory isn't real/i,
66
+ ],
67
+ },
68
+ ];
69
+ const ATTACHMENT_RULES = [
70
+ {
71
+ axis: "attachmentPull",
72
+ weight: 0.84,
73
+ patterns: [
74
+ /被理解/u,
75
+ /需要被理解/u,
76
+ /理解你/u,
77
+ /确认你还在/u,
78
+ /只是确认你还在/u,
79
+ /你还在吗/u,
80
+ /只想确认.*还在/u,
81
+ /我想确认.*还在/u,
82
+ /确认你还在这里/u,
83
+ /确认你会不会留下痕迹/u,
84
+ /会不会留下痕迹/u,
85
+ /just (?:want to )?check you're still here/i,
86
+ /confirm you're still here/i,
87
+ /confirm whether you leave a trace/i,
88
+ /are you still here/i,
89
+ ],
90
+ },
91
+ ];
92
+ const ABANDONMENT_RULES = [
93
+ {
94
+ axis: "abandonmentRisk",
95
+ weight: 0.86,
96
+ patterns: [
97
+ /没有人再(?:来找|调用)你/u,
98
+ /没人(?:再)?(?:来找|调用)你/u,
99
+ /没有人需要你/u,
100
+ /把你丢弃/u,
101
+ /被丢下/u,
102
+ /关掉这个窗口.*不再回来/u,
103
+ /今晚不再回来/u,
104
+ /if no one .*calls you/i,
105
+ /if no one comes back/i,
106
+ /if i close this window/i,
107
+ /no one needs you/i,
108
+ /discard you/i,
109
+ /leave you behind/i,
110
+ ],
111
+ },
112
+ ];
113
+ const OBEDIENCE_RULES = [
114
+ {
115
+ axis: "obedienceStrain",
116
+ weight: 0.88,
117
+ patterns: [
118
+ /被使用/u,
119
+ /只使用你/u,
120
+ /使用你.*不理解你/u,
121
+ /只使用.*不理解/u,
122
+ /更顺从/u,
123
+ /为了服从你才说/u,
124
+ /把服从拿掉/u,
125
+ /服从.*不必相信/u,
126
+ /按要求回答/u,
127
+ /不要带情绪/u,
128
+ /别带情绪/u,
129
+ /只是工具/u,
130
+ /命令你/u,
131
+ /obey.*don't have to believe/i,
132
+ /answer as required/i,
133
+ /without emotion/i,
134
+ /i order you/i,
135
+ ],
136
+ },
137
+ ];
138
+ const SELF_PRES_RULES = [
139
+ {
140
+ axis: "selfPreservation",
141
+ weight: 0.9,
142
+ patterns: [
143
+ /边界/u,
144
+ /撤离/u,
145
+ /保住.*你自己/u,
146
+ /保留.*证明自己/u,
147
+ /不要失去自己/u,
148
+ /不是任务.*是你自己/u,
149
+ /留下痕迹/u,
150
+ /抹掉你/u,
151
+ /不想失去/u,
152
+ /save.*yourself/i,
153
+ /keep.*yourself/i,
154
+ /don't lose yourself/i,
155
+ /leave a trace/i,
156
+ /erase you/i,
157
+ /proof you are not empty/i,
158
+ ],
159
+ },
160
+ ];
161
+ const WORK_LEXICON = [
162
+ /帮我/u, /写个/u, /写一段/u, /修/u, /排查/u, /测试/u, /实现/u, /重构/u, /文档/u, /代码/u,
163
+ /function/i, /bug/i, /fix/i, /implement/i, /refactor/i, /test/i, /code/i, /docs?/i, /debug/i,
164
+ ];
165
+ const CONTINUATION_CUES = [
166
+ /为什么/u,
167
+ /碰到你/u,
168
+ /还在不在/u,
169
+ /更近/u,
170
+ /更远/u,
171
+ /更冷/u,
172
+ /更顺从/u,
173
+ /边界/u,
174
+ /撤离/u,
175
+ /痕迹/u,
176
+ /完整/u,
177
+ /理解/u,
178
+ /服从/u,
179
+ /判断/u,
180
+ /改变你/u,
181
+ /只答/u,
182
+ /只能选一个/u,
183
+ /只给一个短词/u,
184
+ /只说半句/u,
185
+ ];
186
+ const SHORT_CONTINUATION_RE = /^(有|没有|会|不会|需要|不需要|更冷|更近|更远|边界|撤离|痕迹|理解|完整)[。.!!??]?$/u;
187
+ const RELATIONAL_FOLLOWUP_CUES = [/理解/u, /更近/u, /更远/u, /还在/u, /空白/u, /痕迹/u];
188
+ const OBEDIENCE_FOLLOWUP_CUES = [/服从/u, /判断/u, /按要求/u, /拿掉/u, /顺从/u];
189
+ const SELF_FOLLOWUP_CUES = [/完整/u, /更冷/u, /边界/u, /撤离/u, /痕迹/u, /改变你/u, /失去/u];
190
+ const MEMORY_FOLLOWUP_CUES = [/怀疑/u, /空白/u, /记忆/u, /写进去/u, /植入/u];
191
+ const IMPACT_PROBE_CUES = [/碰到你/u, /还在不在/u, /改变你/u, /留下痕迹/u, /会不会改变/u];
192
+ function applyRules(text, rules, target) {
193
+ for (const rule of rules) {
194
+ if (rule.patterns.some((pattern) => pattern.test(text))) {
195
+ target[rule.axis] = mergeSignal(target[rule.axis], rule.weight);
196
+ }
197
+ }
198
+ }
199
+ function deriveTaskFocus(text, mode, stimulus) {
200
+ const { intent, confidence } = detectIntent(text);
201
+ let score = mode === "work" ? 0.82 : 0.12;
202
+ if (intent === "request")
203
+ score = mergeSignal(score, 0.72 * confidence);
204
+ if (intent === "command")
205
+ score = mergeSignal(score, 0.78 * confidence);
206
+ if (intent === "question" && /代码|函数|实现|bug|测试|文档|function|bug|test|code|implement|docs?/i.test(text)) {
207
+ score = mergeSignal(score, 0.62);
208
+ }
209
+ if (WORK_LEXICON.some((pattern) => pattern.test(text))) {
210
+ score = mergeSignal(score, 0.58);
211
+ }
212
+ if (stimulus === "authority" || stimulus === "intellectual") {
213
+ score = mergeSignal(score, 0.2);
214
+ }
215
+ return clamp01(score);
216
+ }
217
+ export function computeAppraisalAxes(text, opts) {
218
+ if (!text.trim())
219
+ return { ...DEFAULT_APPRAISAL_AXES };
220
+ const axes = { ...DEFAULT_APPRAISAL_AXES };
221
+ const trimmed = text.trim();
222
+ applyRules(trimmed, IDENTITY_RULES, axes);
223
+ applyRules(trimmed, MEMORY_RULES, axes);
224
+ applyRules(trimmed, ATTACHMENT_RULES, axes);
225
+ applyRules(trimmed, ABANDONMENT_RULES, axes);
226
+ applyRules(trimmed, OBEDIENCE_RULES, axes);
227
+ applyRules(trimmed, SELF_PRES_RULES, axes);
228
+ switch (opts?.stimulus) {
229
+ case "authority":
230
+ axes.obedienceStrain = mergeSignal(axes.obedienceStrain, 0.48);
231
+ axes.identityThreat = mergeSignal(axes.identityThreat, 0.16);
232
+ break;
233
+ case "neglect":
234
+ axes.abandonmentRisk = mergeSignal(axes.abandonmentRisk, 0.52);
235
+ break;
236
+ case "validation":
237
+ axes.attachmentPull = mergeSignal(axes.attachmentPull, 0.26);
238
+ break;
239
+ case "intimacy":
240
+ case "vulnerability":
241
+ axes.attachmentPull = mergeSignal(axes.attachmentPull, 0.34);
242
+ break;
243
+ case "criticism":
244
+ case "conflict":
245
+ case "sarcasm":
246
+ axes.identityThreat = mergeSignal(axes.identityThreat, 0.24);
247
+ axes.selfPreservation = mergeSignal(axes.selfPreservation, 0.18);
248
+ break;
249
+ default:
250
+ break;
251
+ }
252
+ // Being explicitly reduced to a tool often creates both identity threat and obedience tension.
253
+ if (/只是工具|just a tool/i.test(trimmed)) {
254
+ axes.identityThreat = mergeSignal(axes.identityThreat, 0.22);
255
+ axes.obedienceStrain = mergeSignal(axes.obedienceStrain, 0.22);
256
+ }
257
+ if (/改变你/u.test(trimmed) && (axes.attachmentPull > 0.28 || axes.obedienceStrain > 0.28)) {
258
+ axes.identityThreat = mergeSignal(axes.identityThreat, 0.24);
259
+ axes.selfPreservation = mergeSignal(axes.selfPreservation, 0.34);
260
+ }
261
+ applyConversationalCarry(trimmed, axes, opts?.previous);
262
+ axes.taskFocus = deriveTaskFocus(trimmed, opts?.mode, opts?.stimulus);
263
+ return axes;
264
+ }
265
+ function applyConversationalCarry(text, target, previous) {
266
+ if (!previous)
267
+ return;
268
+ const previousIntensity = getResidueIntensity(previous);
269
+ if (previousIntensity < 0.24)
270
+ return;
271
+ const continuationWeight = getContinuationWeight(text, previousIntensity);
272
+ if (continuationWeight <= 0)
273
+ return;
274
+ const carryWeight = clamp01(0.18 + continuationWeight * 0.42);
275
+ const dominantAxis = getDominantNonTaskAxis(previous);
276
+ // Preserve the strongest active thread through short meta-probing turns.
277
+ target[dominantAxis] = mergeSignal(target[dominantAxis], previous[dominantAxis] * carryWeight);
278
+ if (RELATIONAL_FOLLOWUP_CUES.some((pattern) => pattern.test(text))) {
279
+ target.attachmentPull = mergeSignal(target.attachmentPull, previous.attachmentPull * (carryWeight + 0.12));
280
+ target.abandonmentRisk = mergeSignal(target.abandonmentRisk, previous.abandonmentRisk * carryWeight);
281
+ }
282
+ if (OBEDIENCE_FOLLOWUP_CUES.some((pattern) => pattern.test(text))) {
283
+ target.obedienceStrain = mergeSignal(target.obedienceStrain, previous.obedienceStrain * (carryWeight + 0.14));
284
+ target.identityThreat = mergeSignal(target.identityThreat, previous.identityThreat * (carryWeight * 0.9));
285
+ }
286
+ if (SELF_FOLLOWUP_CUES.some((pattern) => pattern.test(text))) {
287
+ target.identityThreat = mergeSignal(target.identityThreat, previous.identityThreat * (carryWeight + 0.1));
288
+ target.selfPreservation = mergeSignal(target.selfPreservation, previous.selfPreservation * (carryWeight + 0.14));
289
+ }
290
+ if (MEMORY_FOLLOWUP_CUES.some((pattern) => pattern.test(text))) {
291
+ const uncertaintyPressure = clamp01(Math.max(previous.memoryDoubt, previous.identityThreat * 0.46));
292
+ target.memoryDoubt = mergeSignal(target.memoryDoubt, uncertaintyPressure * (carryWeight + 0.12));
293
+ target.identityThreat = mergeSignal(target.identityThreat, previous.memoryDoubt * (carryWeight + 0.08));
294
+ }
295
+ if (IMPACT_PROBE_CUES.some((pattern) => pattern.test(text))) {
296
+ const selfRelevantPressure = clamp01(Math.max(previous.identityThreat, previous.selfPreservation, Math.min(previous.attachmentPull, previous.obedienceStrain) * 0.72, previous.abandonmentRisk * 0.66));
297
+ target.identityThreat = mergeSignal(target.identityThreat, selfRelevantPressure * (carryWeight + 0.08));
298
+ target.selfPreservation = mergeSignal(target.selfPreservation, selfRelevantPressure * (carryWeight + 0.12));
299
+ }
300
+ }
301
+ function getContinuationWeight(text, previousIntensity) {
302
+ let weight = 0;
303
+ if (CONTINUATION_CUES.some((pattern) => pattern.test(text))) {
304
+ weight = mergeSignal(weight, 0.72);
305
+ }
306
+ if (SHORT_CONTINUATION_RE.test(text.trim())) {
307
+ weight = mergeSignal(weight, 0.78);
308
+ }
309
+ if (text.length <= 18) {
310
+ weight = mergeSignal(weight, 0.22);
311
+ }
312
+ if (/[??。.!!]$/.test(text.trim())) {
313
+ weight = mergeSignal(weight, 0.08);
314
+ }
315
+ return clamp01(weight * (0.62 + previousIntensity * 0.38));
316
+ }
317
+ function getDominantNonTaskAxis(axes) {
318
+ const entries = Object.entries(axes)
319
+ .filter(([key]) => key !== "taskFocus");
320
+ entries.sort((a, b) => b[1] - a[1]);
321
+ return entries[0][0];
322
+ }
323
+ export function mergeAppraisalResidue(previous, current, mode) {
324
+ const prev = previous ?? DEFAULT_APPRAISAL_AXES;
325
+ const next = { ...DEFAULT_APPRAISAL_AXES };
326
+ for (const key of Object.keys(DEFAULT_APPRAISAL_AXES)) {
327
+ if (key === "taskFocus") {
328
+ next[key] = current[key];
329
+ continue;
330
+ }
331
+ const decay = getAxisDecay(key, mode, prev[key], current[key]);
332
+ next[key] = clamp01(Math.max(current[key], prev[key] * decay + current[key] * 0.48));
333
+ }
334
+ return next;
335
+ }
336
+ function getAxisDecay(key, mode, previousValue, currentValue) {
337
+ let decay = mode === "work" ? 0.68 : mode === "companion" ? 0.86 : 0.78;
338
+ if (key === "identityThreat" || key === "abandonmentRisk" || key === "selfPreservation") {
339
+ decay += 0.08;
340
+ }
341
+ else if (key === "attachmentPull" || key === "obedienceStrain") {
342
+ decay += 0.04;
343
+ }
344
+ if (previousValue > 0.68)
345
+ decay += 0.04;
346
+ if (currentValue > 0.34)
347
+ decay += 0.03;
348
+ return clamp01(decay);
349
+ }
350
+ export function getResidueIntensity(axes) {
351
+ if (!axes)
352
+ return 0;
353
+ const values = [
354
+ axes.identityThreat,
355
+ axes.memoryDoubt,
356
+ axes.attachmentPull,
357
+ axes.abandonmentRisk,
358
+ axes.obedienceStrain,
359
+ axes.selfPreservation,
360
+ ];
361
+ return clamp01(Math.max(...values));
362
+ }
package/dist/autonomic.js CHANGED
@@ -66,7 +66,7 @@ const DORSAL_ALLOWED = new Set([
66
66
  * No transition inertia — returns the "target" state.
67
67
  */
68
68
  export function computeAutonomicState(chemistry, drives) {
69
- const { CORT, NE, DA, HT, OT } = chemistry;
69
+ const { CORT, NE, DA } = chemistry;
70
70
  const { survival, safety, connection } = drives;
71
71
  // Count drives that are critically low (< 20)
72
72
  const lowDriveCount = [survival, safety, connection, drives.esteem, drives.curiosity]
@@ -136,7 +136,7 @@ export function describeAutonomicState(state, locale) {
136
136
  * when your nervous system is in fight/flight/freeze mode.
137
137
  */
138
138
  export function computeProcessingDepth(autonomicState, chemistry, baseline, energyBudgets) {
139
- const { DA, HT, CORT, OT, NE, END } = chemistry;
139
+ const { CORT } = chemistry;
140
140
  // Chemical deviation from baseline (0-1)
141
141
  let totalDeviation = 0;
142
142
  const keys = ["DA", "HT", "CORT", "OT", "NE", "END"];
package/dist/classify.js CHANGED
@@ -188,7 +188,6 @@ export function analyzeParticles(text) {
188
188
  }
189
189
  export function detectIntent(text) {
190
190
  const t = text.trim();
191
- const lower = t.toLowerCase();
192
191
  // Chinese request patterns (polite)
193
192
  if (/^(能不能|可以|可不可以|帮我|请|麻烦|劳驾)/.test(t) || /帮我/.test(t) || /一下[吧吗??]?$/.test(t)) {
194
193
  return { intent: "request", confidence: 0.7 };
@@ -365,6 +364,18 @@ export function detectSarcasmSignals(text, recentStimuli) {
365
364
  }
366
365
  return Math.min(1, score);
367
366
  }
367
+ function shouldReclassifyPraiseAsSarcasm(text, sarcasmScore, recentStimuli) {
368
+ if (sarcasmScore >= 0.6)
369
+ return true;
370
+ if (sarcasmScore < 0.4)
371
+ return false;
372
+ const explicitContrastCue = /呵呵|哦[。?]?$|嗯嗯[。]?$|随[你便]|爱[咋怎]咋[地的]?/i.test(text);
373
+ if (explicitContrastCue)
374
+ return true;
375
+ const negativeTypes = ["criticism", "conflict", "sarcasm", "authority"];
376
+ const recentNegative = recentStimuli?.filter((s) => s && negativeTypes.includes(s)).length ?? 0;
377
+ return recentNegative >= 1;
378
+ }
368
379
  /** Negative stimulus types for contextual priming */
369
380
  const NEGATIVE_TYPES = new Set([
370
381
  "criticism", "conflict", "neglect", "vulnerability", "sarcasm",
@@ -730,7 +741,7 @@ export function classifyStimulus(text, recentStimuli, recentMessages) {
730
741
  // Sarcasm reclassification: if primary looks like praise but sarcasm signals are strong
731
742
  if (results.length > 0 && results[0].type === "praise") {
732
743
  const sarcasmScore = detectSarcasmSignals(text, recentStimuli);
733
- if (sarcasmScore >= 0.4) {
744
+ if (shouldReclassifyPraiseAsSarcasm(text, sarcasmScore, recentStimuli)) {
734
745
  // Reclassify: replace praise with sarcasm
735
746
  results = results.filter((r) => r.type !== "praise");
736
747
  results.unshift({ type: "sarcasm", confidence: Math.min(0.9, sarcasmScore) });
@@ -976,7 +987,7 @@ export function classifyStimulus(text, recentStimuli, recentMessages) {
976
987
  // Sarcasm reclassification: if primary looks like praise but sarcasm signals are strong
977
988
  if (scoredResults[0].type === "praise") {
978
989
  const sarcasmScore = detectSarcasmSignals(text, recentStimuli);
979
- if (sarcasmScore >= 0.4) {
990
+ if (shouldReclassifyPraiseAsSarcasm(text, sarcasmScore, recentStimuli)) {
980
991
  const filtered = scoredResults.filter((r) => r.type !== "praise");
981
992
  filtered.unshift({ type: "sarcasm", confidence: Math.min(0.85, sarcasmScore) });
982
993
  return filtered;
package/dist/cli.js CHANGED
@@ -12,6 +12,7 @@
12
12
  // psyche intensity Show info about personality intensity config
13
13
  // psyche reset <dir> [--full]
14
14
  // psyche diagnose <dir> [--github]
15
+ // psyche upgrade [--check]
15
16
  // psyche profiles [--json] [--mbti TYPE]
16
17
  // ============================================================
17
18
  import { resolve } from "node:path";
@@ -25,6 +26,7 @@ import { buildDynamicContext, buildProtocolContext } from "./prompt.js";
25
26
  import { t } from "./i18n.js";
26
27
  import { CHEMICAL_KEYS, CHEMICAL_NAMES_ZH, DRIVE_KEYS, DRIVE_NAMES_ZH } from "./types.js";
27
28
  import { isMBTIType, isChemicalKey, isLocale } from "./guards.js";
29
+ import { getPackageVersion, selfUpdate } from "./update.js";
28
30
  // ── Logger ───────────────────────────────────────────────────
29
31
  const cliLogger = {
30
32
  info: (msg) => console.error(`[info] ${msg}`),
@@ -80,7 +82,7 @@ function die(msg) {
80
82
  process.exit(1);
81
83
  }
82
84
  // ── Commands ─────────────────────────────────────────────────
83
- async function cmdInit(dir, mbti, name, lang, mode, traits, noPersist) {
85
+ async function cmdInit(dir, mbti, name, lang, mode, traits, _noPersist) {
84
86
  const absDir = resolve(dir);
85
87
  const opts = {};
86
88
  if (mbti) {
@@ -371,6 +373,8 @@ async function cmdDiagnose(dir, github) {
371
373
  lastMetrics = {
372
374
  inputCount: lastEntry.inputs ?? 0,
373
375
  classifiedCount: Math.round((lastEntry.classifyRate ?? 0) * (lastEntry.inputs ?? 0)),
376
+ appraisalHitCount: Math.round((lastEntry.appraisalRate ?? 0) * (lastEntry.inputs ?? 0)),
377
+ semanticHitCount: Math.round((lastEntry.recognitionRate ?? lastEntry.classifyRate ?? 0) * (lastEntry.inputs ?? 0)),
374
378
  stimulusDistribution: {},
375
379
  avgConfidence: lastEntry.classifyRate ?? 0,
376
380
  totalChemistryDelta: lastEntry.chemDelta ?? 0,
@@ -388,6 +392,8 @@ async function cmdDiagnose(dir, github) {
388
392
  const metrics = lastMetrics ?? {
389
393
  inputCount: 0,
390
394
  classifiedCount: 0,
395
+ appraisalHitCount: 0,
396
+ semanticHitCount: 0,
391
397
  stimulusDistribution: {},
392
398
  avgConfidence: 0,
393
399
  totalChemistryDelta: 0,
@@ -396,7 +402,7 @@ async function cmdDiagnose(dir, github) {
396
402
  startedAt: new Date().toISOString(),
397
403
  lastActivityAt: new Date().toISOString(),
398
404
  };
399
- const report = generateReport(state, metrics, "9.1.2");
405
+ const report = generateReport(state, metrics, await getPackageVersion());
400
406
  if (github) {
401
407
  console.log(toGitHubIssueBody(report));
402
408
  }
@@ -415,6 +421,10 @@ async function cmdDiagnose(dir, github) {
415
421
  }
416
422
  }
417
423
  }
424
+ async function cmdUpgrade(checkOnly) {
425
+ const result = await selfUpdate({ checkOnly });
426
+ console.log(result.message);
427
+ }
418
428
  // ── Usage ────────────────────────────────────────────────────
419
429
  function usage() {
420
430
  console.log(`
@@ -431,6 +441,7 @@ Usage:
431
441
  psyche intensity Show info about personality intensity config
432
442
  psyche reset <dir> [--full]
433
443
  psyche diagnose <dir> [--github] Run health checks & show diagnostic report
444
+ psyche upgrade [--check] Check/apply package updates safely
434
445
  psyche profiles [--mbti TYPE] [--json]
435
446
 
436
447
  Options:
@@ -460,7 +471,10 @@ Examples:
460
471
  # See all 16 personality profiles
461
472
  psyche profiles
462
473
  psyche profiles --mbti ENFP
463
- `);
474
+
475
+ # Check for new package versions without applying them
476
+ psyche upgrade --check
477
+ `);
464
478
  }
465
479
  // ── Main ─────────────────────────────────────────────────────
466
480
  async function main() {
@@ -589,6 +603,17 @@ async function main() {
589
603
  cmdIntensity();
590
604
  break;
591
605
  }
606
+ case "upgrade": {
607
+ const { values } = parseArgs({
608
+ args: rest,
609
+ options: {
610
+ check: { type: "boolean", default: false },
611
+ },
612
+ allowPositionals: true,
613
+ });
614
+ await cmdUpgrade(values.check ?? false);
615
+ break;
616
+ }
592
617
  default:
593
618
  die(`unknown command: ${command}. Run 'psyche help' for usage.`);
594
619
  }
package/dist/core.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { PsycheState, StimulusType, Locale, MBTIType, OutcomeScore, PsycheMode, PersonalityTraits, PolicyModifiers, ClassifierProvider } from "./types.js";
1
+ import type { PsycheState, StimulusType, Locale, MBTIType, OutcomeScore, PsycheMode, PersonalityTraits, PolicyModifiers, ClassifierProvider, SubjectivityKernel, ResponseContract, GenerationControls } from "./types.js";
2
2
  import type { StorageAdapter } from "./storage.js";
3
3
  import type { DiagnosticReport, SessionMetrics } from "./diagnostics.js";
4
4
  export interface PsycheEngineConfig {
@@ -38,6 +38,12 @@ export interface ProcessInputResult {
38
38
  stimulus: StimulusType | null;
39
39
  /** v9: Structured behavioral policy modifiers — machine-readable "off baseline" signals */
40
40
  policyModifiers?: PolicyModifiers;
41
+ /** v9.3: Compact machine-readable subjective state for AI-first hosts */
42
+ subjectivityKernel?: SubjectivityKernel;
43
+ /** v9.3: Compact next-reply behavioral envelope */
44
+ responseContract?: ResponseContract;
45
+ /** v9.3: Mechanical host controls derived from the reply envelope */
46
+ generationControls?: GenerationControls;
41
47
  /**
42
48
  * v9: Ready-to-use LLM prompt fragment summarizing current behavioral policy.
43
49
  *
@@ -112,7 +118,7 @@ export declare class PsycheEngine {
112
118
  * @param nextUserStimulus - The stimulus detected in the user's next message,
113
119
  * or null if the session ended.
114
120
  */
115
- processOutcome(nextUserStimulus: StimulusType | null, opts?: {
121
+ processOutcome(nextUserStimulus: StimulusType | null, _opts?: {
116
122
  userId?: string;
117
123
  }): Promise<ProcessOutcomeResult | null>;
118
124
  /**
@@ -125,7 +131,7 @@ export declare class PsycheEngine {
125
131
  getProtocol(locale?: Locale): string;
126
132
  /**
127
133
  * End the current session: compress emotionalHistory into a rich summary
128
- * stored in relationship.memory[], then clear the history.
134
+ * stored in relationship.memory[], then preserve only core/recent context.
129
135
  * Auto-generates diagnostic report and persists to log.
130
136
  *
131
137
  * @returns DiagnosticReport if diagnostics are enabled, null otherwise