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.
@@ -1,8 +1,17 @@
1
1
  // ============================================================
2
- // Diagnostics — Automatic health monitoring & feedback collection
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
- // Issues + suggestions
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 issue of report.issues) {
313
- lines.push(` ${SEVERITY_ICON[issue.severity]} ${issue.id}: ${issue.message}`);
314
- if (issue.detail) {
315
- for (const d of issue.detail.split("\n")) {
316
- lines.push(` ${d}`);
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
- mbti: report.mbti,
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 MBTI personality baseline.
24
+ * to the personality baseline.
25
25
  *
26
- * When drives are satisfied, effective baseline = MBTI 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(mbtiBaseline: ChemicalState, drives: InnateDrives, traitDrift?: TraitDriftState): ChemicalState;
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 MBTI personality baseline.
117
+ * to the personality baseline.
118
118
  *
119
- * When drives are satisfied, effective baseline = MBTI 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(mbtiBaseline, drives, traitDrift) {
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 MBTI baseline, clamp to [0, 100]
171
- const effective = { ...mbtiBaseline };
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, mbtiBaseline[key] + delta[key]));
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 MBTI profile vectors. Returns predicted chemistry,
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;
@@ -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 MBTI profile vectors. Returns predicted chemistry,
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 MBTI profile alone
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 MBTI + chemical signature ──
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 MBTI dimensions
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
- // Extract MBTI dimensions
473
- const isIntro = mbti[0] === "I";
474
- const isIntuitive = mbti[1] === "N";
475
- const isFeeling = mbti[2] === "F";
476
- // Build trait fragments based on MBTI + chemical state
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, MBTIType, Locale, StimulusType, ChemicalState, ChemicalSnapshot, SelfModel, RelationshipState, EmpathyEntry, EmotionPattern, DriveType, InnateDrives, LearningState, LearnedVectorAdjustment, PredictionRecord, OutcomeScore, OutcomeSignals, AttachmentStyle, AttachmentData, MetacognitiveState, RegulationRecord, DefensePatternRecord, RegulationStrategyType, DefenseMechanismType, PersonhoodState, PersistedCausalInsight, GrowthDirection, PersonalityTraits, PsycheMode, PolicyModifiers, SubjectivityKernel, ResponseContract, GenerationControls, AppraisalAxes, SubjectResidue, TaskPlaneState, SubjectPlaneState, RelationPlaneState, AmbiguityPlaneState, RelationMoveType, RelationMove, OpenLoopType, OpenLoopState, PendingRelationSignalState, DyadicFieldState, SessionBridgeState, ThrongletsExportSubject, ThrongletsExportPrimitive, ThrongletsExportBase, RelationMilestoneExport, OpenLoopAnchorExport, WritebackCalibrationExport, ContinuityAnchorExport, ThrongletsExport, ThrongletsExportState, ExternalContinuityEvent, ExternalContinuityEnvelope, ThrongletsTraceTaxonomy, ThrongletsExternalContinuityRecord, ThrongletsTracePayload, ThrongletsTraceSerializationOptions, WritebackSignalType, WritebackSignalWeightMap, PendingWritebackCalibration, WritebackCalibrationFeedback, WritebackCalibrationMetric, TraitDriftState, EnergyBudgets, ClassifierProvider, ClassifierContext, ClassificationResult, } from "./types.js";
6
- export { CHEMICAL_KEYS, CHEMICAL_NAMES, CHEMICAL_NAMES_ZH, DEFAULT_RELATIONSHIP, DEFAULT_DRIVES, DEFAULT_LEARNING_STATE, DEFAULT_METACOGNITIVE_STATE, DEFAULT_PERSONHOOD_STATE, DEFAULT_ATTACHMENT, DRIVE_KEYS, DRIVE_NAMES_ZH, DEFAULT_TRAIT_DRIFT, DEFAULT_ENERGY_BUDGETS, DEFAULT_APPRAISAL_AXES, DEFAULT_SUBJECT_RESIDUE, DEFAULT_DYADIC_FIELD, } from "./types.js";
7
- export { computeSelfReflection, computeEmotionalTendency, buildSelfReflectionContext } from "./self-recognition.js";
8
- export type { SelfReflection } from "./self-recognition.js";
9
- export { PsycheInteraction } from "./interaction.js";
10
- export type { ExchangeResult, ContagionResult, RelationshipSummary, InteractionPhase } from "./interaction.js";
11
- export { getChannelProfile, buildChannelModifier, createCustomChannel } from "./channels.js";
12
- export type { ChannelType, ChannelProfile } from "./channels.js";
13
- export { createCustomProfile, validateProfileConfig, PRESET_PROFILES } from "./custom-profile.js";
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";