psyche-ai 9.2.9 → 10.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -3
- package/dist/cli.js +8 -3
- package/dist/core.d.ts +4 -2
- package/dist/core.js +28 -31
- package/dist/diagnostics.d.ts +52 -1
- package/dist/diagnostics.js +248 -17
- package/dist/drives.d.ts +3 -3
- package/dist/drives.js +6 -6
- package/dist/generative-self.d.ts +1 -1
- package/dist/generative-self.js +9 -10
- package/dist/index.d.ts +9 -57
- package/dist/index.js +175 -60
- package/dist/observability.d.ts +14 -0
- package/dist/observability.js +392 -0
- package/dist/prompt.d.ts +23 -6
- package/dist/prompt.js +296 -140
- package/dist/psyche-file.js +18 -7
- package/dist/relation-dynamics.js +41 -19
- package/dist/types.d.ts +152 -6
- package/dist/types.js +5 -0
- package/llms.txt +19 -0
- package/openclaw.plugin.json +2 -2
- package/package.json +1 -1
- package/server.json +3 -3
package/dist/diagnostics.js
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
// ============================================================
|
|
2
|
-
// Diagnostics —
|
|
2
|
+
// Diagnostics — Layered health monitoring & feedback collection
|
|
3
|
+
//
|
|
4
|
+
// Four diagnostic layers matching the frozen stack architecture:
|
|
5
|
+
// L1: Subjective continuity (Psyche)
|
|
6
|
+
// L2: Delegate continuity (Psyche → Thronglets boundary)
|
|
7
|
+
// L3: Policy / orchestration (Oasyce Net — structural only)
|
|
8
|
+
// L4: Public truth / finality (Oasyce Chain — structural only)
|
|
9
|
+
//
|
|
10
|
+
// L1 and L2 are fully implemented inside Psyche.
|
|
11
|
+
// L3 and L4 define interfaces only — implementation lives
|
|
12
|
+
// outside Psyche. This separation is intentional: Psyche must
|
|
13
|
+
// not reach into layers it does not own.
|
|
3
14
|
//
|
|
4
|
-
// Runs transparently during normal use. Detects anomalies,
|
|
5
|
-
// collects session metrics, generates issue-ready reports.
|
|
6
15
|
// Zero dependencies. Privacy-first — no message content logged.
|
|
7
16
|
// ============================================================
|
|
8
17
|
import { CHEMICAL_KEYS, DRIVE_KEYS } from "./types.js";
|
|
@@ -10,12 +19,14 @@ import { detectEmotions } from "./chemistry.js";
|
|
|
10
19
|
// ── Health Checks ────────────────────────────────────────────
|
|
11
20
|
export function runHealthCheck(state) {
|
|
12
21
|
const issues = [];
|
|
22
|
+
// ── L1: Subjective Continuity ─────────────────────────────
|
|
13
23
|
// 1. Chemistry out of bounds — clamp() missed somewhere
|
|
14
24
|
for (const key of CHEMICAL_KEYS) {
|
|
15
25
|
const val = state.current[key];
|
|
16
26
|
if (val < 0 || val > 100) {
|
|
17
27
|
issues.push({
|
|
18
28
|
id: "CHEM_OOB",
|
|
29
|
+
layer: "subjective-continuity",
|
|
19
30
|
severity: "critical",
|
|
20
31
|
message: `${key} out of bounds: ${val.toFixed(1)}`,
|
|
21
32
|
detail: `Expected 0-100, got ${val}`,
|
|
@@ -29,6 +40,7 @@ export function runHealthCheck(state) {
|
|
|
29
40
|
if (val < 0 || val > 100) {
|
|
30
41
|
issues.push({
|
|
31
42
|
id: "DRIVE_OOB",
|
|
43
|
+
layer: "subjective-continuity",
|
|
32
44
|
severity: "critical",
|
|
33
45
|
message: `Drive '${key}' out of bounds: ${val.toFixed(1)}`,
|
|
34
46
|
suggestion: `drives.ts feedDrives/decayDrives 缺少边界检查`,
|
|
@@ -40,6 +52,7 @@ export function runHealthCheck(state) {
|
|
|
40
52
|
if (criticalDrives.length >= 3) {
|
|
41
53
|
issues.push({
|
|
42
54
|
id: "DRIVES_COLLAPSE",
|
|
55
|
+
layer: "subjective-continuity",
|
|
43
56
|
severity: "warning",
|
|
44
57
|
message: `${criticalDrives.length}/5 drives below 15: ${criticalDrives.join(", ")}`,
|
|
45
58
|
suggestion: `decayDrives 衰减太猛。考虑加一个下限(比如 10),或者在 initialize 时根据距上次对话时间做 recovery`,
|
|
@@ -49,6 +62,7 @@ export function runHealthCheck(state) {
|
|
|
49
62
|
if (state.agreementStreak > 10 && !state.lastDisagreement) {
|
|
50
63
|
issues.push({
|
|
51
64
|
id: "SYCOPHANCY_RISK",
|
|
65
|
+
layer: "subjective-continuity",
|
|
52
66
|
severity: "warning",
|
|
53
67
|
message: `Agreement streak at ${state.agreementStreak}, never disagreed`,
|
|
54
68
|
suggestion: `updateAgreementStreak 的检测逻辑可能没覆盖到实际 LLM 输出格式。检查 psyche-file.ts 里的正则是否匹配当前 provider 的回复风格`,
|
|
@@ -61,6 +75,7 @@ export function runHealthCheck(state) {
|
|
|
61
75
|
if (nullCount === history.length) {
|
|
62
76
|
issues.push({
|
|
63
77
|
id: "CLASSIFIER_DEAD",
|
|
78
|
+
layer: "subjective-continuity",
|
|
64
79
|
severity: "critical",
|
|
65
80
|
message: `All ${history.length} recent snapshots have null stimulus`,
|
|
66
81
|
suggestion: `classify.ts 对当前用户的输入模式完全无效。收集 null 样本补充 SHORT_MESSAGE_MAP,或降低 llmClassifierThreshold 让 LLM fallback 兜底`,
|
|
@@ -69,6 +84,7 @@ export function runHealthCheck(state) {
|
|
|
69
84
|
else if (nullCount / history.length > 0.7) {
|
|
70
85
|
issues.push({
|
|
71
86
|
id: "CLASSIFIER_WEAK",
|
|
87
|
+
layer: "subjective-continuity",
|
|
72
88
|
severity: "warning",
|
|
73
89
|
message: `${nullCount}/${history.length} snapshots (${Math.round(nullCount / history.length * 100)}%) have null stimulus`,
|
|
74
90
|
suggestion: `分类命中率低于 30%。看 stimulusDistribution 里哪些类型被识别了,缺的类型需要补 classify.ts 规则或扩 SHORT_MESSAGE_MAP`,
|
|
@@ -80,6 +96,7 @@ export function runHealthCheck(state) {
|
|
|
80
96
|
if (state.meta.totalInteractions > 10 && chemDelta < 3) {
|
|
81
97
|
issues.push({
|
|
82
98
|
id: "CHEM_FROZEN",
|
|
99
|
+
layer: "subjective-continuity",
|
|
83
100
|
severity: "warning",
|
|
84
101
|
message: `Chemistry delta only ${chemDelta.toFixed(1)} after ${state.meta.totalInteractions} interactions`,
|
|
85
102
|
suggestion: `两种可能:1) classifier 全 null 导致 applyStimulus 从不触发 2) decay 太快把变化抹平。检查 emotionalHistory 里是否有 non-null stimulus`,
|
|
@@ -90,6 +107,7 @@ export function runHealthCheck(state) {
|
|
|
90
107
|
if (state.meta.totalInteractions > 5 && emotions.length === 0) {
|
|
91
108
|
issues.push({
|
|
92
109
|
id: "NO_EMOTIONS",
|
|
110
|
+
layer: "subjective-continuity",
|
|
93
111
|
severity: "info",
|
|
94
112
|
message: "No emergent emotions after 5+ interactions",
|
|
95
113
|
suggestion: `chemistry.ts detectEmotions 的阈值可能太严。或者 maxChemicalDelta 太小(当前上限导致化学值永远在窄区间波动)`,
|
|
@@ -102,6 +120,7 @@ export function runHealthCheck(state) {
|
|
|
102
120
|
if (unique.size === 1 && rel.memory.length > 3) {
|
|
103
121
|
issues.push({
|
|
104
122
|
id: "MEMORY_CORRUPT",
|
|
123
|
+
layer: "subjective-continuity",
|
|
105
124
|
severity: "warning",
|
|
106
125
|
message: `Relationship '${userId}' has ${rel.memory.length} identical memory entries`,
|
|
107
126
|
suggestion: `compressSession 的摘要逻辑在 emotionalHistory 过短时会生成相同文本。加去重或在压缩前检查 unique`,
|
|
@@ -113,6 +132,7 @@ export function runHealthCheck(state) {
|
|
|
113
132
|
if (state.version < 6) {
|
|
114
133
|
issues.push({
|
|
115
134
|
id: "STATE_OUTDATED",
|
|
135
|
+
layer: "subjective-continuity",
|
|
116
136
|
severity: "info",
|
|
117
137
|
message: `State version ${state.version}, expected 6+`,
|
|
118
138
|
suggestion: `migrateToLatest 应该在 initialize 时自动跑。如果没跑,检查 storage.ts load() 是否走到了 migrateToLatest 分支`,
|
|
@@ -127,6 +147,7 @@ export function runHealthCheck(state) {
|
|
|
127
147
|
const only = classified[0].stimulus;
|
|
128
148
|
issues.push({
|
|
129
149
|
id: "STIMULUS_MONOTONE",
|
|
150
|
+
layer: "subjective-continuity",
|
|
130
151
|
severity: "info",
|
|
131
152
|
message: `All ${classified.length} classified inputs → ${only}`,
|
|
132
153
|
suggestion: `classify.ts 某条规则优先级太高,把所有输入都吃掉了。检查 RULES 里 ${only} 相关的正则是否过于宽泛`,
|
|
@@ -140,13 +161,178 @@ export function runHealthCheck(state) {
|
|
|
140
161
|
&& defaultRel.trust === 50 && defaultRel.intimacy === 30) {
|
|
141
162
|
issues.push({
|
|
142
163
|
id: "RELATIONSHIP_STALE",
|
|
164
|
+
layer: "subjective-continuity",
|
|
143
165
|
severity: "info",
|
|
144
166
|
message: `Trust/intimacy unchanged after ${state.meta.totalInteractions} interactions`,
|
|
145
167
|
suggestion: `processOutput 里的 mergeUpdates 没有更新 relationship。可能 LLM 从来没输出 <psyche_update> 里的 trust/intimacy 字段,或 contagion 没触发 relationship 变化`,
|
|
146
168
|
});
|
|
147
169
|
}
|
|
170
|
+
// ── L1: Trait drift trajectory ────────────────────────────
|
|
171
|
+
const drift = state.traitDrift;
|
|
172
|
+
if (drift && drift.sessionCount > 5) {
|
|
173
|
+
const totalDelta = Object.values(drift.baselineDelta ?? {}).reduce((sum, v) => sum + Math.abs(v ?? 0), 0);
|
|
174
|
+
if (totalDelta < 0.5) {
|
|
175
|
+
issues.push({
|
|
176
|
+
id: "DRIFT_STAGNANT",
|
|
177
|
+
layer: "subjective-continuity",
|
|
178
|
+
severity: "info",
|
|
179
|
+
message: `Trait drift near zero after ${drift.sessionCount} sessions (total delta: ${totalDelta.toFixed(2)})`,
|
|
180
|
+
suggestion: `accumulator 值可能太低触发不了 drift。检查 drives.ts updateTraitDrift 里的阈值`,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// ── L1: Dyadic field coherence ────────────────────────────
|
|
185
|
+
const dyadicFields = state.dyadicFields ?? {};
|
|
186
|
+
for (const [key, field] of Object.entries(dyadicFields)) {
|
|
187
|
+
// Contradictory state: high closeness + high boundary pressure = tension
|
|
188
|
+
// This is ALLOWED (ambiguity), but flag if combined with zero unfinished tension
|
|
189
|
+
if (field.perceivedCloseness > 0.7 && field.boundaryPressure > 0.7
|
|
190
|
+
&& field.unfinishedTension < 0.1) {
|
|
191
|
+
issues.push({
|
|
192
|
+
id: "DYADIC_INCOHERENT",
|
|
193
|
+
layer: "subjective-continuity",
|
|
194
|
+
severity: "warning",
|
|
195
|
+
message: `Relation '${key}': high closeness (${field.perceivedCloseness.toFixed(2)}) + high boundary pressure (${field.boundaryPressure.toFixed(2)}) but no tension (${field.unfinishedTension.toFixed(2)})`,
|
|
196
|
+
suggestion: `relation-dynamics.ts 的 evolveField 可能在某条路径下压住了 unfinishedTension 而没压 closeness/boundary。这三个量应该联动`,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
// Stale open loops — loops that are very old but still active
|
|
200
|
+
const staleLoops = (field.openLoops ?? []).filter(l => l.ageTurns > 20 && l.intensity > 0.3);
|
|
201
|
+
if (staleLoops.length > 0) {
|
|
202
|
+
issues.push({
|
|
203
|
+
id: "LOOPS_STALE",
|
|
204
|
+
layer: "subjective-continuity",
|
|
205
|
+
severity: "info",
|
|
206
|
+
message: `Relation '${key}': ${staleLoops.length} open loop(s) older than 20 turns still active`,
|
|
207
|
+
suggestion: `open loop decay 可能太慢。检查 relation-dynamics.ts 里 loop aging 的衰减率`,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// ── L1: Energy budget depletion ───────────────────────────
|
|
212
|
+
const energy = state.energyBudgets;
|
|
213
|
+
if (energy) {
|
|
214
|
+
const depleted = [
|
|
215
|
+
energy.attention < 10 && "attention",
|
|
216
|
+
energy.socialEnergy < 10 && "socialEnergy",
|
|
217
|
+
energy.decisionCapacity < 10 && "decisionCapacity",
|
|
218
|
+
].filter(Boolean);
|
|
219
|
+
if (depleted.length >= 2) {
|
|
220
|
+
issues.push({
|
|
221
|
+
id: "ENERGY_DEPLETED",
|
|
222
|
+
layer: "subjective-continuity",
|
|
223
|
+
severity: "warning",
|
|
224
|
+
message: `${depleted.length}/3 energy budgets critically low: ${depleted.join(", ")}`,
|
|
225
|
+
suggestion: `agent 需要"休息"。如果 host 支持,建议缩短回复或增加恢复间隔`,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// ── L2: Delegate Continuity (Boundary Probe) ──────────────
|
|
230
|
+
// Writeback calibration — diverging signals indicate the learning loop is not converging
|
|
231
|
+
const wbFeedback = state.lastWritebackFeedback ?? [];
|
|
232
|
+
if (wbFeedback.length > 0) {
|
|
233
|
+
const diverging = wbFeedback.filter(f => f.effect === "diverging");
|
|
234
|
+
if (diverging.length > wbFeedback.length * 0.5 && wbFeedback.length >= 3) {
|
|
235
|
+
issues.push({
|
|
236
|
+
id: "WRITEBACK_DIVERGING",
|
|
237
|
+
layer: "delegate-continuity",
|
|
238
|
+
severity: "warning",
|
|
239
|
+
message: `${diverging.length}/${wbFeedback.length} writeback signals diverging`,
|
|
240
|
+
detail: diverging.map(d => `${d.signal}: ${d.metric} baseline=${d.baseline.toFixed(2)} current=${d.current.toFixed(2)}`).join("\n"),
|
|
241
|
+
suggestion: `写回信号效果与预期方向相反。可能是 host 输出未真正执行 Psyche 的回应契约,或 signalWeights 需要重新校准`,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// Pending writeback calibrations — too many pending = loop stalled
|
|
246
|
+
const pendingCals = state.pendingWritebackCalibrations ?? [];
|
|
247
|
+
if (pendingCals.length > 8) {
|
|
248
|
+
issues.push({
|
|
249
|
+
id: "WRITEBACK_BACKLOG",
|
|
250
|
+
layer: "delegate-continuity",
|
|
251
|
+
severity: "info",
|
|
252
|
+
message: `${pendingCals.length} writeback calibrations pending (backlog)`,
|
|
253
|
+
suggestion: `校准回路积压。可能是 host 不调用 processOutput 或调用频率太低`,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
// Thronglets export state — check boundary health
|
|
257
|
+
const exportState = state.throngletsExportState;
|
|
258
|
+
if (exportState && exportState.lastAt) {
|
|
259
|
+
const lastExportAge = Date.now() - new Date(exportState.lastAt).getTime();
|
|
260
|
+
// If last export was >24h ago and there has been significant interaction since
|
|
261
|
+
if (lastExportAge > 86_400_000 && state.meta.totalInteractions > 10) {
|
|
262
|
+
issues.push({
|
|
263
|
+
id: "EXPORT_STALE",
|
|
264
|
+
layer: "delegate-continuity",
|
|
265
|
+
severity: "info",
|
|
266
|
+
message: `Last Thronglets export >24h ago despite ${state.meta.totalInteractions} interactions`,
|
|
267
|
+
suggestion: `external continuity exports are not being produced. Check if Thronglets adapter is connected and external continuity is enabled`,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
148
271
|
return issues;
|
|
149
272
|
}
|
|
273
|
+
// ── Layer Health Computation ────────────────────────────────
|
|
274
|
+
function computeLayerHealth(issues, layer) {
|
|
275
|
+
const layerIssues = issues.filter(i => i.layer === layer);
|
|
276
|
+
const worst = layerIssues.reduce((acc, i) => {
|
|
277
|
+
if (!acc)
|
|
278
|
+
return i.severity;
|
|
279
|
+
if (i.severity === "critical")
|
|
280
|
+
return "critical";
|
|
281
|
+
if (i.severity === "warning" && acc !== "critical")
|
|
282
|
+
return "warning";
|
|
283
|
+
return acc;
|
|
284
|
+
}, null);
|
|
285
|
+
const status = worst === "critical" ? "failing"
|
|
286
|
+
: worst === "warning" ? "degraded"
|
|
287
|
+
: "healthy";
|
|
288
|
+
const summary = layerIssues.length === 0
|
|
289
|
+
? "No issues detected"
|
|
290
|
+
: `${layerIssues.length} issue(s): ${layerIssues.map(i => i.id).join(", ")}`;
|
|
291
|
+
return { status, worstSeverity: worst, issueCount: layerIssues.length, summary };
|
|
292
|
+
}
|
|
293
|
+
export function computeLayerHealthSummary(state, issues) {
|
|
294
|
+
// L1: Subjective continuity measurements
|
|
295
|
+
const chemDeviation = CHEMICAL_KEYS.reduce((sum, k) => sum + Math.abs(state.current[k] - state.baseline[k]), 0);
|
|
296
|
+
const drift = state.traitDrift;
|
|
297
|
+
const driftEstablished = !!(drift && drift.sessionCount > 0
|
|
298
|
+
&& Object.values(drift.baselineDelta ?? {}).some(v => Math.abs(v ?? 0) > 0.1));
|
|
299
|
+
const dyadicFields = state.dyadicFields ?? {};
|
|
300
|
+
const activeDyadic = Object.values(dyadicFields).filter(f => f.perceivedCloseness > 0.05 || f.boundaryPressure > 0.05 || f.unfinishedTension > 0.05).length;
|
|
301
|
+
const learning = state.learning;
|
|
302
|
+
const predError = learning.predictionHistory.length > 0
|
|
303
|
+
? learning.predictionHistory.reduce((sum, p) => sum + p.predictionError, 0) / learning.predictionHistory.length
|
|
304
|
+
: 1;
|
|
305
|
+
// L2: Delegate continuity measurements
|
|
306
|
+
const wbFeedback = state.lastWritebackFeedback ?? [];
|
|
307
|
+
const effects = { converging: 0, holding: 0, diverging: 0 };
|
|
308
|
+
for (const f of wbFeedback)
|
|
309
|
+
effects[f.effect]++;
|
|
310
|
+
const exportState = state.throngletsExportState;
|
|
311
|
+
const exportConnected = !!(exportState && exportState.lastAt);
|
|
312
|
+
return {
|
|
313
|
+
"subjective-continuity": {
|
|
314
|
+
...computeLayerHealth(issues, "subjective-continuity"),
|
|
315
|
+
chemistryDeviation: chemDeviation,
|
|
316
|
+
sessionBridgeActive: !!(state.sessionStartedAt),
|
|
317
|
+
traitDriftEstablished: driftEstablished,
|
|
318
|
+
activeDyadicRelations: activeDyadic,
|
|
319
|
+
predictionError: predError,
|
|
320
|
+
},
|
|
321
|
+
"delegate-continuity": {
|
|
322
|
+
...computeLayerHealth(issues, "delegate-continuity"),
|
|
323
|
+
externalContinuityConnected: exportConnected,
|
|
324
|
+
pendingExports: exportState?.lastKeys?.length ?? 0,
|
|
325
|
+
writebackLoopActive: wbFeedback.length > 0,
|
|
326
|
+
calibrationEffects: effects,
|
|
327
|
+
},
|
|
328
|
+
"policy-orchestration": {
|
|
329
|
+
...computeLayerHealth(issues, "policy-orchestration"),
|
|
330
|
+
},
|
|
331
|
+
"public-truth": {
|
|
332
|
+
...computeLayerHealth(issues, "public-truth"),
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
}
|
|
150
336
|
// ── Session Collector ────────────────────────────────────────
|
|
151
337
|
export class DiagnosticCollector {
|
|
152
338
|
metrics;
|
|
@@ -229,10 +415,11 @@ function hasSemanticAppraisal(appraisal) {
|
|
|
229
415
|
// ── Report Generation ────────────────────────────────────────
|
|
230
416
|
export function generateReport(state, metrics, packageVersion) {
|
|
231
417
|
const issues = runHealthCheck(state);
|
|
232
|
-
// Session-level issues
|
|
418
|
+
// Session-level issues (all L1 — they measure subjective experience quality within a session)
|
|
233
419
|
if (metrics.inputCount >= 5 && metrics.semanticHitCount === 0) {
|
|
234
420
|
issues.push({
|
|
235
421
|
id: "SESSION_NO_RECOGNITION",
|
|
422
|
+
layer: "subjective-continuity",
|
|
236
423
|
severity: "critical",
|
|
237
424
|
message: `0/${metrics.inputCount} inputs produced any semantic recognition this session`,
|
|
238
425
|
suggestion: `整条识别链都没工作:先看 OpenClaw 输入文本是否带元数据包装,再看 classify.ts 和 appraisal.ts 是否真的吃到了净化后的用户原文`,
|
|
@@ -241,6 +428,7 @@ export function generateReport(state, metrics, packageVersion) {
|
|
|
241
428
|
else if (metrics.inputCount >= 5 && metrics.classifiedCount === 0 && metrics.semanticHitCount > 0) {
|
|
242
429
|
issues.push({
|
|
243
430
|
id: "SESSION_APPRAISAL_ONLY",
|
|
431
|
+
layer: "subjective-continuity",
|
|
244
432
|
severity: "info",
|
|
245
433
|
message: `0/${metrics.inputCount} inputs hit legacy stimulus labels, but ${metrics.semanticHitCount}/${metrics.inputCount} hit appraisal axes`,
|
|
246
434
|
suggestion: `这说明主体识别在工作,但旧 stimulus taxonomy 没覆盖这类输入。是否继续补 classify.ts,取决于你是否还把 stimulus 当主要观测口径`,
|
|
@@ -249,6 +437,7 @@ export function generateReport(state, metrics, packageVersion) {
|
|
|
249
437
|
if (metrics.inputCount >= 3 && metrics.totalChemistryDelta < 1) {
|
|
250
438
|
issues.push({
|
|
251
439
|
id: "SESSION_FLAT",
|
|
440
|
+
layer: "subjective-continuity",
|
|
252
441
|
severity: "warning",
|
|
253
442
|
message: `${metrics.inputCount} inputs but chemistry barely moved (delta: ${metrics.totalChemistryDelta.toFixed(1)})`,
|
|
254
443
|
suggestion: `用户感受不到情感变化。可能原因:1) maxChemicalDelta 配置太低 2) work mode 把变化压到了 0.3x 3) personalityIntensity 太低`,
|
|
@@ -257,6 +446,7 @@ export function generateReport(state, metrics, packageVersion) {
|
|
|
257
446
|
if (metrics.errors.length > 0) {
|
|
258
447
|
issues.push({
|
|
259
448
|
id: "SESSION_ERRORS",
|
|
449
|
+
layer: "subjective-continuity",
|
|
260
450
|
severity: metrics.errors.length > 3 ? "critical" : "warning",
|
|
261
451
|
message: `${metrics.errors.length} error(s) during session`,
|
|
262
452
|
detail: metrics.errors.map(e => `[${e.phase}] ${e.message}`).join("\n"),
|
|
@@ -268,17 +458,28 @@ export function generateReport(state, metrics, packageVersion) {
|
|
|
268
458
|
if (metrics.classifiedCount >= 5 && stimTypes.length === 1) {
|
|
269
459
|
issues.push({
|
|
270
460
|
id: "SESSION_MONOCLASS",
|
|
461
|
+
layer: "subjective-continuity",
|
|
271
462
|
severity: "info",
|
|
272
463
|
message: `All ${metrics.classifiedCount} classified inputs → ${stimTypes[0]}`,
|
|
273
464
|
suggestion: `单一分类说明 classify.ts 里 ${stimTypes[0]} 的规则吃掉了所有输入。检查该类型的正则优先级`,
|
|
274
465
|
});
|
|
275
466
|
}
|
|
467
|
+
// Compute layered view
|
|
468
|
+
const layeredIssues = {
|
|
469
|
+
"subjective-continuity": issues.filter(i => i.layer === "subjective-continuity"),
|
|
470
|
+
"delegate-continuity": issues.filter(i => i.layer === "delegate-continuity"),
|
|
471
|
+
"policy-orchestration": issues.filter(i => i.layer === "policy-orchestration"),
|
|
472
|
+
"public-truth": issues.filter(i => i.layer === "public-truth"),
|
|
473
|
+
};
|
|
474
|
+
const layerHealth = computeLayerHealthSummary(state, issues);
|
|
276
475
|
return {
|
|
277
476
|
version: packageVersion,
|
|
278
477
|
timestamp: new Date().toISOString(),
|
|
279
478
|
agent: state.meta.agentName,
|
|
280
|
-
mbti: state.mbti,
|
|
479
|
+
mbti: state.mbti ?? undefined,
|
|
281
480
|
issues,
|
|
481
|
+
layeredIssues,
|
|
482
|
+
layerHealth,
|
|
282
483
|
metrics,
|
|
283
484
|
stateSnapshot: {
|
|
284
485
|
chemistry: { ...state.current },
|
|
@@ -298,26 +499,55 @@ const SEVERITY_ICON = {
|
|
|
298
499
|
warning: "[! ]",
|
|
299
500
|
info: "[i ]",
|
|
300
501
|
};
|
|
502
|
+
const LAYER_STATUS_ICON = {
|
|
503
|
+
healthy: "[ok]",
|
|
504
|
+
degraded: "[~~]",
|
|
505
|
+
failing: "[!!]",
|
|
506
|
+
};
|
|
507
|
+
const LAYER_LABELS = {
|
|
508
|
+
"subjective-continuity": "L1 subjective-continuity",
|
|
509
|
+
"delegate-continuity": "L2 delegate-continuity",
|
|
510
|
+
"policy-orchestration": "L3 policy-orchestration",
|
|
511
|
+
"public-truth": "L4 public-truth",
|
|
512
|
+
};
|
|
301
513
|
export function formatReport(report) {
|
|
302
514
|
const lines = [];
|
|
303
515
|
lines.push(`psyche-ai diagnostic report v${report.version}`);
|
|
304
|
-
lines.push(`agent: ${report.agent} (${report.mbti}) | ${report.timestamp}`);
|
|
516
|
+
lines.push(`agent: ${report.agent}${report.mbti ? ` (${report.mbti})` : ""} | ${report.timestamp}`);
|
|
305
517
|
lines.push("─".repeat(60));
|
|
306
|
-
//
|
|
518
|
+
// Layer health overview — the first thing to read
|
|
519
|
+
lines.push("\n layer health:");
|
|
520
|
+
for (const layer of ["subjective-continuity", "delegate-continuity", "policy-orchestration", "public-truth"]) {
|
|
521
|
+
const h = report.layerHealth[layer];
|
|
522
|
+
lines.push(` ${LAYER_STATUS_ICON[h.status]} ${LAYER_LABELS[layer]}: ${h.summary}`);
|
|
523
|
+
}
|
|
524
|
+
// L1 details
|
|
525
|
+
const l1 = report.layerHealth["subjective-continuity"];
|
|
526
|
+
lines.push(`\n L1 detail: chemDev=${l1.chemistryDeviation.toFixed(1)} bridge=${l1.sessionBridgeActive} drift=${l1.traitDriftEstablished} dyadic=${l1.activeDyadicRelations} predErr=${l1.predictionError.toFixed(2)}`);
|
|
527
|
+
// L2 details
|
|
528
|
+
const l2 = report.layerHealth["delegate-continuity"];
|
|
529
|
+
lines.push(` L2 detail: extContinuity=${l2.externalContinuityConnected} exports=${l2.pendingExports} writeback=${l2.writebackLoopActive} cal=${l2.calibrationEffects.converging}c/${l2.calibrationEffects.holding}h/${l2.calibrationEffects.diverging}d`);
|
|
530
|
+
// Issues by layer
|
|
307
531
|
if (report.issues.length === 0) {
|
|
308
532
|
lines.push("\n No issues detected. System healthy.");
|
|
309
533
|
}
|
|
310
534
|
else {
|
|
311
535
|
lines.push(`\n ${report.issues.length} issue(s) found:\n`);
|
|
312
|
-
for (const
|
|
313
|
-
|
|
314
|
-
if (
|
|
315
|
-
|
|
316
|
-
|
|
536
|
+
for (const layer of ["subjective-continuity", "delegate-continuity", "policy-orchestration", "public-truth"]) {
|
|
537
|
+
const layerIssues = report.layeredIssues[layer];
|
|
538
|
+
if (layerIssues.length === 0)
|
|
539
|
+
continue;
|
|
540
|
+
lines.push(` ── ${LAYER_LABELS[layer]} ──`);
|
|
541
|
+
for (const issue of layerIssues) {
|
|
542
|
+
lines.push(` ${SEVERITY_ICON[issue.severity]} ${issue.id}: ${issue.message}`);
|
|
543
|
+
if (issue.detail) {
|
|
544
|
+
for (const d of issue.detail.split("\n")) {
|
|
545
|
+
lines.push(` ${d}`);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
if (issue.suggestion) {
|
|
549
|
+
lines.push(` → ${issue.suggestion}`);
|
|
317
550
|
}
|
|
318
|
-
}
|
|
319
|
-
if (issue.suggestion) {
|
|
320
|
-
lines.push(` → ${issue.suggestion}`);
|
|
321
551
|
}
|
|
322
552
|
}
|
|
323
553
|
}
|
|
@@ -369,7 +599,7 @@ export function toGitHubIssueBody(report) {
|
|
|
369
599
|
lines.push("## Auto-Diagnostic Report");
|
|
370
600
|
lines.push("");
|
|
371
601
|
lines.push(`- **psyche-ai**: v${report.version}`);
|
|
372
|
-
lines.push(`- **Agent**: ${report.agent} (${report.mbti})`);
|
|
602
|
+
lines.push(`- **Agent**: ${report.agent}${report.mbti ? ` (${report.mbti})` : ""}`);
|
|
373
603
|
lines.push(`- **Generated**: ${report.timestamp}`);
|
|
374
604
|
lines.push("");
|
|
375
605
|
// Issues
|
|
@@ -467,7 +697,8 @@ export async function submitFeedback(report, url, timeout = 5000) {
|
|
|
467
697
|
const payload = {
|
|
468
698
|
version: report.version,
|
|
469
699
|
timestamp: report.timestamp,
|
|
470
|
-
|
|
700
|
+
agent: report.agent,
|
|
701
|
+
mbti: report.mbti ?? "N/A",
|
|
471
702
|
issues: actionable.map(i => ({
|
|
472
703
|
id: i.id,
|
|
473
704
|
severity: i.severity,
|
package/dist/drives.d.ts
CHANGED
|
@@ -21,12 +21,12 @@ export declare function detectExistentialThreat(text: string): number;
|
|
|
21
21
|
export declare function computeMaslowWeights(drives: InnateDrives): Record<DriveType, number>;
|
|
22
22
|
/**
|
|
23
23
|
* Compute the effective baseline by applying drive-based deltas
|
|
24
|
-
* to the
|
|
24
|
+
* to the personality baseline.
|
|
25
25
|
*
|
|
26
|
-
* When drives are satisfied, effective baseline =
|
|
26
|
+
* When drives are satisfied, effective baseline = personality baseline.
|
|
27
27
|
* When drives are unsatisfied, baseline shifts to reflect the unmet need.
|
|
28
28
|
*/
|
|
29
|
-
export declare function computeEffectiveBaseline(
|
|
29
|
+
export declare function computeEffectiveBaseline(baseline: ChemicalState, drives: InnateDrives, traitDrift?: TraitDriftState): ChemicalState;
|
|
30
30
|
/**
|
|
31
31
|
* Compute effective sensitivity for a given stimulus.
|
|
32
32
|
* Unsatisfied drives amplify relevant stimuli (up to +40%).
|
package/dist/drives.js
CHANGED
|
@@ -114,12 +114,12 @@ export function computeMaslowWeights(drives) {
|
|
|
114
114
|
// This is the core mechanism: drives pull chemistry in a direction.
|
|
115
115
|
/**
|
|
116
116
|
* Compute the effective baseline by applying drive-based deltas
|
|
117
|
-
* to the
|
|
117
|
+
* to the personality baseline.
|
|
118
118
|
*
|
|
119
|
-
* When drives are satisfied, effective baseline =
|
|
119
|
+
* When drives are satisfied, effective baseline = personality baseline.
|
|
120
120
|
* When drives are unsatisfied, baseline shifts to reflect the unmet need.
|
|
121
121
|
*/
|
|
122
|
-
export function computeEffectiveBaseline(
|
|
122
|
+
export function computeEffectiveBaseline(baseline, drives, traitDrift) {
|
|
123
123
|
const delta = { DA: 0, HT: 0, CORT: 0, OT: 0, NE: 0, END: 0 };
|
|
124
124
|
const weights = computeMaslowWeights(drives);
|
|
125
125
|
// L1: Survival threat → fight-or-flight (CORT↑↑, NE↑, OT↓)
|
|
@@ -167,10 +167,10 @@ export function computeEffectiveBaseline(mbtiBaseline, drives, traitDrift) {
|
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
|
-
// Apply deltas to
|
|
171
|
-
const effective = { ...
|
|
170
|
+
// Apply deltas to personality baseline, clamp to [0, 100]
|
|
171
|
+
const effective = { ...baseline };
|
|
172
172
|
for (const key of CHEMICAL_KEYS) {
|
|
173
|
-
effective[key] = Math.max(0, Math.min(100,
|
|
173
|
+
effective[key] = Math.max(0, Math.min(100, baseline[key] + delta[key]));
|
|
174
174
|
}
|
|
175
175
|
return effective;
|
|
176
176
|
}
|
|
@@ -66,7 +66,7 @@ export declare function computeGenerativeSelf(state: PsycheState): GenerativeSel
|
|
|
66
66
|
* Predict own emotional reaction to a hypothetical stimulus.
|
|
67
67
|
*
|
|
68
68
|
* Uses learned vectors if available for context, otherwise falls back
|
|
69
|
-
* to the baseline
|
|
69
|
+
* to the baseline profile vectors. Returns predicted chemistry,
|
|
70
70
|
* dominant emotion label, and confidence.
|
|
71
71
|
*/
|
|
72
72
|
export declare function predictSelfReaction(state: PsycheState, stimulus: StimulusType, locale?: Locale): SelfPrediction;
|
package/dist/generative-self.js
CHANGED
|
@@ -48,7 +48,7 @@ export function computeGenerativeSelf(state) {
|
|
|
48
48
|
* Predict own emotional reaction to a hypothetical stimulus.
|
|
49
49
|
*
|
|
50
50
|
* Uses learned vectors if available for context, otherwise falls back
|
|
51
|
-
* to the baseline
|
|
51
|
+
* to the baseline profile vectors. Returns predicted chemistry,
|
|
52
52
|
* dominant emotion label, and confidence.
|
|
53
53
|
*/
|
|
54
54
|
export function predictSelfReaction(state, stimulus, locale = "en") {
|
|
@@ -65,7 +65,7 @@ export function predictSelfReaction(state, stimulus, locale = "en") {
|
|
|
65
65
|
const learned = findBestLearnedVector(state.learning, stimulus);
|
|
66
66
|
// Build the effective vector: base + learned adjustment
|
|
67
67
|
const effectiveVector = { ...base };
|
|
68
|
-
let confidence = 0.3; // baseline confidence from
|
|
68
|
+
let confidence = 0.3; // baseline confidence from profile alone
|
|
69
69
|
if (learned) {
|
|
70
70
|
for (const key of CHEMICAL_KEYS) {
|
|
71
71
|
const adj = learned.adjustment[key] ?? 0;
|
|
@@ -200,7 +200,7 @@ export function detectInternalConflicts(state, locale = "en") {
|
|
|
200
200
|
export function buildIdentityNarrative(state, insights, growthArc, locale = "en") {
|
|
201
201
|
const isZh = locale === "zh";
|
|
202
202
|
const parts = [];
|
|
203
|
-
// ── Sentence 1: Core personality from
|
|
203
|
+
// ── Sentence 1: Core personality from baseline chemistry ──
|
|
204
204
|
const coreTraits = describeCoreTraits(state, isZh);
|
|
205
205
|
parts.push(coreTraits);
|
|
206
206
|
// ── Sentence 2: Strongest causal insight (what I've learned) ──
|
|
@@ -463,17 +463,16 @@ function computeGrowthArc(state, locale) {
|
|
|
463
463
|
}
|
|
464
464
|
// ── Internal: Core Trait Description ────────────────────────
|
|
465
465
|
/**
|
|
466
|
-
* Describe the agent's core personality traits from
|
|
466
|
+
* Describe the agent's core personality traits from baseline chemistry
|
|
467
467
|
* and current chemical signature.
|
|
468
468
|
*/
|
|
469
469
|
function describeCoreTraits(state, isZh) {
|
|
470
|
-
const mbti = state.mbti;
|
|
471
470
|
const chem = state.current;
|
|
472
|
-
//
|
|
473
|
-
const isIntro =
|
|
474
|
-
const isIntuitive =
|
|
475
|
-
const isFeeling =
|
|
476
|
-
// Build trait fragments based on
|
|
471
|
+
// Derive personality dimensions from baseline chemistry
|
|
472
|
+
const isIntro = state.baseline.DA < 55; // low DA baseline → introverted
|
|
473
|
+
const isIntuitive = state.baseline.DA > state.baseline.HT; // novelty over stability → intuitive
|
|
474
|
+
const isFeeling = state.baseline.OT >= 50; // warm baseline → feeling
|
|
475
|
+
// Build trait fragments based on baseline + chemical state
|
|
477
476
|
const fragments = [];
|
|
478
477
|
// Energy orientation
|
|
479
478
|
if (isIntro) {
|
package/dist/index.d.ts
CHANGED
|
@@ -2,60 +2,12 @@ export { PsycheEngine } from "./core.js";
|
|
|
2
2
|
export type { PsycheEngineConfig, ProcessInputResult, ProcessOutputResult, ProcessOutcomeResult } from "./core.js";
|
|
3
3
|
export { FileStorageAdapter, MemoryStorageAdapter } from "./storage.js";
|
|
4
4
|
export type { StorageAdapter } from "./storage.js";
|
|
5
|
-
export type { PsycheState,
|
|
6
|
-
export {
|
|
7
|
-
|
|
8
|
-
export
|
|
9
|
-
export {
|
|
10
|
-
export
|
|
11
|
-
export {
|
|
12
|
-
export
|
|
13
|
-
export {
|
|
14
|
-
export type { CustomProfileConfig, ResolvedProfile } from "./custom-profile.js";
|
|
15
|
-
export { evaluateOutcome, getLearnedVector, updateLearnedVector, computeContextHash, predictChemistry, computePredictionError, recordPrediction, getAveragePredictionError, } from "./learning.js";
|
|
16
|
-
export { classifyStimulusWithContext, extractContextFeatures, stimulusWarmth } from "./context-classifier.js";
|
|
17
|
-
export type { ContextFeatures, ContextualClassification } from "./context-classifier.js";
|
|
18
|
-
export { predictNextStimulus, generateAnticipation, computeSurpriseEffect, computeRegret, } from "./temporal.js";
|
|
19
|
-
export type { StimulusPrediction, AnticipationState, RegretEntry } from "./temporal.js";
|
|
20
|
-
export { updateAttachment, computeSeparationEffect, computeReunionEffect, } from "./attachment.js";
|
|
21
|
-
export type { SeparationEffect } from "./attachment.js";
|
|
22
|
-
export { assessMetacognition, computeEmotionalConfidence, generateRegulationSuggestions, detectDefenseMechanisms, } from "./metacognition.js";
|
|
23
|
-
export type { MetacognitiveAssessment, RegulationSuggestion, DetectedDefense } from "./metacognition.js";
|
|
24
|
-
export { computeDecisionBias, computeAttentionWeights, computeExploreExploit, buildDecisionContext, computePolicyModifiers, buildPolicyContext, } from "./decision-bias.js";
|
|
25
|
-
export type { DecisionBiasVector, AttentionWeights } from "./decision-bias.js";
|
|
26
|
-
export { computeSubjectivityKernel, buildSubjectivityContext } from "./subjectivity.js";
|
|
27
|
-
export { computeResponseContract, buildResponseContractContext } from "./response-contract.js";
|
|
28
|
-
export { deriveGenerationControls } from "./host-controls.js";
|
|
29
|
-
export { deriveReplyEnvelope } from "./reply-envelope.js";
|
|
30
|
-
export type { ReplyEnvelope } from "./reply-envelope.js";
|
|
31
|
-
export { computeAppraisalAxes, mergeAppraisalResidue, getResidueIntensity } from "./appraisal.js";
|
|
32
|
-
export { computeRelationMove, evolveDyadicField, evolvePendingRelationSignals, getLoopPressure, applySessionBridge, applyWritebackSignals, createWritebackCalibrations, evaluateWritebackCalibrations, } from "./relation-dynamics.js";
|
|
33
|
-
export { EXTERNAL_CONTINUITY_SIGNAL_KINDS, EXTERNAL_CONTINUITY_TRACE_KINDS, buildExternalContinuityEnvelope, } from "./external-continuity.js";
|
|
34
|
-
export { deriveThrongletsExports } from "./thronglets-export.js";
|
|
35
|
-
export { taxonomyForThrongletsExport, serializeThrongletsExportAsTrace, serializeExternalContinuityForThronglets, } from "./thronglets-runtime.js";
|
|
36
|
-
export { runRuntimeProbe } from "./runtime-probe.js";
|
|
37
|
-
export type { RuntimeProbeResult } from "./runtime-probe.js";
|
|
38
|
-
export { computeExperientialField, computeCoherence, detectUnnamedEmotion, computeAffectCore } from "./experiential-field.js";
|
|
39
|
-
export type { ExperientialField, ExperientialQuality, ConstructionContext } from "./experiential-field.js";
|
|
40
|
-
export { computeGenerativeSelf, predictSelfReaction, detectInternalConflicts, buildIdentityNarrative } from "./generative-self.js";
|
|
41
|
-
export type { GenerativeSelfModel, CausalInsight, SelfPrediction, GrowthArc, InternalConflict } from "./generative-self.js";
|
|
42
|
-
export { updateSharedIntentionality, estimateOtherMood, buildSharedIntentionalityContext } from "./shared-intentionality.js";
|
|
43
|
-
export type { SharedIntentionalityState, TheoryOfMindModel, JointAttentionTopic, GoalAlignment } from "./shared-intentionality.js";
|
|
44
|
-
export { assessEthics, detectIntermittentReinforcement, detectDependencyRisk, buildEthicalContext, } from "./ethics.js";
|
|
45
|
-
export type { EthicalAssessment, EthicalConcern, SelfProtectionAction } from "./ethics.js";
|
|
46
|
-
export { computeAutonomicResult, computeAutonomicState, computeProcessingDepth, gateEmotions, getTransitionTime, describeAutonomicState } from "./autonomic.js";
|
|
47
|
-
export type { AutonomicState, AutonomicResult, AutonomicTransition } from "./autonomic.js";
|
|
48
|
-
export { computeCircadianModulation, computeHomeostaticPressure, getCircadianPhase, computeEnergyDepletion, computeEnergyRecovery } from "./circadian.js";
|
|
49
|
-
export type { CircadianPhase } from "./circadian.js";
|
|
50
|
-
export { computePrimarySystems, computeSystemInteractions, gatePrimarySystemsByAutonomic, getDominantSystems, describeBehavioralTendencies, PRIMARY_SYSTEM_NAMES, } from "./primary-systems.js";
|
|
51
|
-
export type { PrimarySystemName, PrimarySystemLevels, BehavioralTendency, DominantSystem, } from "./primary-systems.js";
|
|
52
|
-
export { updateTraitDrift } from "./drives.js";
|
|
53
|
-
export { classifyStimulus, getPrimaryStimulus, scoreSentiment, scoreEmoji, BuiltInClassifier, analyzeParticles, detectIntent, buildLLMClassifierPrompt, parseLLMClassification } from "./classify.js";
|
|
54
|
-
export type { StimulusClassification, ParticleSignal, MessageIntent } from "./classify.js";
|
|
55
|
-
export { buildProtocolContext, buildDynamicContext, buildCompactContext, isNearBaseline, getNearBaselineThreshold } from "./prompt.js";
|
|
56
|
-
export { describeEmotionalState, getExpressionHint, getBehaviorGuide, detectEmotions } from "./chemistry.js";
|
|
57
|
-
export { getBaseline, getTemperament, getSensitivity, getDefaultSelfModel, traitsToBaseline, mbtiToTraits } from "./profiles.js";
|
|
58
|
-
export { migrateToLatest, compressSession, parsePsycheUpdate, computeSnapshotIntensity, computeSnapshotValence, consolidateHistory, retrieveRelatedMemories, } from "./psyche-file.js";
|
|
59
|
-
export type { PsycheUpdateResult } from "./psyche-file.js";
|
|
60
|
-
export { runHealthCheck, DiagnosticCollector, generateReport, formatReport, toGitHubIssueBody, formatLogEntry, submitFeedback, } from "./diagnostics.js";
|
|
61
|
-
export type { DiagnosticIssue, DiagnosticReport, SessionMetrics, Severity } from "./diagnostics.js";
|
|
5
|
+
export type { PsycheState, ChemicalState, Locale, PsycheMode, StimulusType, MBTIType, WritebackSignalType, DelegateCapability, CapabilityGrant, RevocationCondition, DelegateAuthorization, } from "./types.js";
|
|
6
|
+
export { buildProtocolContext, buildCompactContext } from "./prompt.js";
|
|
7
|
+
/** @deprecated Use buildCompactContext instead. Kept for backward compat. */
|
|
8
|
+
export { buildDynamicContext } from "./prompt.js";
|
|
9
|
+
export { isNearBaseline, getNearBaselineThreshold, deriveBehavioralBias, computeUserInvestment } from "./prompt.js";
|
|
10
|
+
export { getBaseline, getSensitivity, getDefaultSelfModel, getTemperament, traitsToBaseline, mbtiToTraits } from "./profiles.js";
|
|
11
|
+
export { createCustomProfile, PRESET_PROFILES } from "./custom-profile.js";
|
|
12
|
+
export { computeLayerHealthSummary } from "./diagnostics.js";
|
|
13
|
+
export type { LayerHealthSummary, LayerHealthDetail, LayerStatus, DiagnosticLayer } from "./diagnostics.js";
|