psyche-ai 9.1.1 → 9.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/http.js +1 -1
- package/dist/adapters/openclaw.js +52 -14
- package/dist/autonomic.js +6 -2
- package/dist/chemistry.js +59 -1
- package/dist/classify.js +168 -1
- package/dist/cli.js +76 -0
- package/dist/core.d.ts +46 -2
- package/dist/core.js +125 -6
- package/dist/decision-bias.d.ts +35 -2
- package/dist/decision-bias.js +98 -1
- package/dist/diagnostics.d.ts +84 -0
- package/dist/diagnostics.js +481 -0
- package/dist/drives.js +5 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/prompt.d.ts +4 -1
- package/dist/prompt.js +23 -7
- package/dist/psyche-file.js +8 -2
- package/dist/storage.d.ts +10 -0
- package/dist/storage.js +22 -1
- package/package.json +1 -1
package/dist/core.js
CHANGED
|
@@ -21,6 +21,7 @@ import { isStimulusType } from "./guards.js";
|
|
|
21
21
|
import { parsePsycheUpdate, mergeUpdates, updateAgreementStreak, pushSnapshot, compressSession, } from "./psyche-file.js";
|
|
22
22
|
import { decayDrives, feedDrives, detectExistentialThreat, computeEffectiveBaseline, computeEffectiveSensitivity, } from "./drives.js";
|
|
23
23
|
import { checkForUpdate } from "./update.js";
|
|
24
|
+
import { DiagnosticCollector, generateReport, formatLogEntry, submitFeedback } from "./diagnostics.js";
|
|
24
25
|
import { evaluateOutcome, computeContextHash, updateLearnedVector, predictChemistry, recordPrediction, } from "./learning.js";
|
|
25
26
|
import { assessMetacognition } from "./metacognition.js";
|
|
26
27
|
import { buildDecisionContext, computePolicyModifiers, buildPolicyContext } from "./decision-bias.js";
|
|
@@ -45,6 +46,12 @@ export class PsycheEngine {
|
|
|
45
46
|
protocolCache = new Map();
|
|
46
47
|
/** Pending prediction from last processInput for auto-learning */
|
|
47
48
|
pendingPrediction = null;
|
|
49
|
+
/** Built-in diagnostics collector — auto-records every processInput/processOutput */
|
|
50
|
+
diagnosticCollector;
|
|
51
|
+
/** Last generated diagnostic report (from endSession or explicit call) */
|
|
52
|
+
lastReport = null;
|
|
53
|
+
/** URL for auto-submitting diagnostic reports */
|
|
54
|
+
feedbackUrl;
|
|
48
55
|
constructor(config = {}, storage) {
|
|
49
56
|
this.traits = config.traits;
|
|
50
57
|
this.classifier = config.classifier ?? new BuiltInClassifier();
|
|
@@ -68,6 +75,12 @@ export class PsycheEngine {
|
|
|
68
75
|
else {
|
|
69
76
|
this.storage = storage;
|
|
70
77
|
}
|
|
78
|
+
// Diagnostics: on by default, opt-out with diagnostics: false
|
|
79
|
+
this.diagnosticCollector = config.diagnostics === false ? null : new DiagnosticCollector();
|
|
80
|
+
if (this.diagnosticCollector) {
|
|
81
|
+
this.diagnosticCollector.onWarning = (msg) => console.warn(`\x1b[33m[Psyche]\x1b[0m ${msg}`);
|
|
82
|
+
}
|
|
83
|
+
this.feedbackUrl = config.feedbackUrl ?? "https://psyche-feedback.wutc.workers.dev";
|
|
71
84
|
}
|
|
72
85
|
/**
|
|
73
86
|
* Load or create initial state. Must be called before processInput/processOutput.
|
|
@@ -220,12 +233,16 @@ export class PsycheEngine {
|
|
|
220
233
|
// Feed drives from stimulus, then apply stimulus with drive-modified sensitivity
|
|
221
234
|
drives = feedDrives(drives, primary.type);
|
|
222
235
|
const effectiveSensitivity = computeEffectiveSensitivity(getSensitivity(state.mbti), drives, primary.type, state.traitDrift);
|
|
236
|
+
// v9.2: Confidence modulates intensity — a 0.95 life-or-death dilemma
|
|
237
|
+
// hits ~1.7x harder than a 0.55 mild disagreement.
|
|
238
|
+
// Maps [0.5, 1.0] → [0.6, 1.2] via linear interpolation.
|
|
239
|
+
const confidenceIntensity = 0.6 + (primary.confidence - 0.5) * 1.2;
|
|
223
240
|
const modeMultiplier = this.cfg.mode === "work" ? 0.3 : this.cfg.mode === "companion" ? 1.5 : 1.0;
|
|
224
241
|
const effectiveMaxDelta = this.cfg.mode === "work" ? 5 : this.cfg.maxChemicalDelta;
|
|
225
242
|
// v9: Habituation — count recent same-type stimuli in this session
|
|
226
243
|
const recentSameCount = (state.emotionalHistory ?? [])
|
|
227
244
|
.filter(s => s.stimulus === primary.type).length + 1; // +1 for current
|
|
228
|
-
current = applyStimulus(current, primary.type, effectiveSensitivity * this.cfg.personalityIntensity * modeMultiplier, effectiveMaxDelta, NOOP_LOGGER, recentSameCount);
|
|
245
|
+
current = applyStimulus(current, primary.type, effectiveSensitivity * this.cfg.personalityIntensity * modeMultiplier * confidenceIntensity, effectiveMaxDelta, NOOP_LOGGER, recentSameCount);
|
|
229
246
|
}
|
|
230
247
|
state = { ...state, drives, current };
|
|
231
248
|
}
|
|
@@ -389,6 +406,10 @@ export class PsycheEngine {
|
|
|
389
406
|
// Persist
|
|
390
407
|
this.state = state;
|
|
391
408
|
await this.storage.save(state);
|
|
409
|
+
// Auto-diagnostics: record this input
|
|
410
|
+
if (this.diagnosticCollector) {
|
|
411
|
+
this.diagnosticCollector.recordInput(appliedStimulus, appliedStimulus ? 1.0 : 0.0, state.current);
|
|
412
|
+
}
|
|
392
413
|
// Build metacognitive and decision context strings
|
|
393
414
|
const metacogNote = metacognitiveAssessment?.metacognitiveNote;
|
|
394
415
|
const decisionCtx = buildDecisionContext(state);
|
|
@@ -397,7 +418,7 @@ export class PsycheEngine {
|
|
|
397
418
|
const experientialNarrative = experientialField?.narrative || undefined;
|
|
398
419
|
// v9: Compute structured policy modifiers
|
|
399
420
|
const policyModifiers = computePolicyModifiers(state);
|
|
400
|
-
const policyCtx = buildPolicyContext(policyModifiers, locale);
|
|
421
|
+
const policyCtx = buildPolicyContext(policyModifiers, locale, state.drives);
|
|
401
422
|
// P10: Append processing depth info to autonomic description when depth is low
|
|
402
423
|
let autonomicDesc;
|
|
403
424
|
if (autonomicResult.state !== "ventral-vagal") {
|
|
@@ -422,11 +443,13 @@ export class PsycheEngine {
|
|
|
422
443
|
sharedIntentionalityContext: sharedCtx || undefined,
|
|
423
444
|
experientialNarrative: experientialNarrative,
|
|
424
445
|
autonomicDescription: autonomicDesc,
|
|
446
|
+
autonomicState: autonomicResult.state,
|
|
425
447
|
primarySystemsDescription: primarySystemsDescription || undefined,
|
|
426
448
|
policyContext: policyCtx || undefined,
|
|
427
449
|
}),
|
|
428
450
|
stimulus: appliedStimulus,
|
|
429
451
|
policyModifiers,
|
|
452
|
+
policyContext: policyCtx,
|
|
430
453
|
};
|
|
431
454
|
}
|
|
432
455
|
return {
|
|
@@ -438,11 +461,13 @@ export class PsycheEngine {
|
|
|
438
461
|
sharedIntentionalityContext: sharedCtx || undefined,
|
|
439
462
|
experientialNarrative: experientialNarrative,
|
|
440
463
|
autonomicDescription: autonomicDesc,
|
|
464
|
+
autonomicState: autonomicResult.state,
|
|
441
465
|
primarySystemsDescription: primarySystemsDescription || undefined,
|
|
442
466
|
policyContext: policyCtx || undefined,
|
|
443
467
|
}),
|
|
444
468
|
stimulus: appliedStimulus,
|
|
445
469
|
policyModifiers,
|
|
470
|
+
policyContext: policyCtx,
|
|
446
471
|
};
|
|
447
472
|
}
|
|
448
473
|
/**
|
|
@@ -463,6 +488,41 @@ export class PsycheEngine {
|
|
|
463
488
|
stateChanged = true;
|
|
464
489
|
}
|
|
465
490
|
}
|
|
491
|
+
// v9.2: Self-expression feedback — the agent's own output reinforces its emotional state.
|
|
492
|
+
// "Saying it makes you feel it more." Reduced rate (0.3x) to avoid runaway loops.
|
|
493
|
+
// Only applies when the text is substantial (> 20 chars) and classifies above threshold.
|
|
494
|
+
if (text.length > 20) {
|
|
495
|
+
const selfClassifications = await Promise.resolve(this.classifier.classify(text, { locale: this.cfg.locale }));
|
|
496
|
+
const selfPrimary = selfClassifications[0];
|
|
497
|
+
if (selfPrimary && selfPrimary.confidence >= 0.5) {
|
|
498
|
+
const selfFeedbackRate = 0.3;
|
|
499
|
+
state = {
|
|
500
|
+
...state,
|
|
501
|
+
current: applyContagion(state.current, selfPrimary.type, selfFeedbackRate, getSensitivity(state.mbti)),
|
|
502
|
+
};
|
|
503
|
+
stateChanged = true;
|
|
504
|
+
// v9.2 P4: Autonomic recovery — expressing vulnerable/comforting emotions
|
|
505
|
+
// while stressed triggers parasympathetic relief (post-cry cortisol drop).
|
|
506
|
+
// Biology: emotional expression activates vagal brake, releasing endorphins
|
|
507
|
+
// and lowering cortisol. The more stressed you are, the more relief you get.
|
|
508
|
+
const RELEASE_TYPES = new Set([
|
|
509
|
+
"vulnerability", "intimacy", "validation",
|
|
510
|
+
]);
|
|
511
|
+
if (RELEASE_TYPES.has(selfPrimary.type) && state.current.CORT > 60) {
|
|
512
|
+
const stressExcess = (state.current.CORT - 60) / 40; // 0 at CORT=60, 1 at CORT=100
|
|
513
|
+
const recoveryMagnitude = 3 + stressExcess * 5; // 3–8 point CORT drop
|
|
514
|
+
state = {
|
|
515
|
+
...state,
|
|
516
|
+
current: {
|
|
517
|
+
...state.current,
|
|
518
|
+
CORT: clamp(state.current.CORT - recoveryMagnitude),
|
|
519
|
+
END: clamp(state.current.END + recoveryMagnitude * 0.6),
|
|
520
|
+
HT: clamp(state.current.HT + recoveryMagnitude * 0.3),
|
|
521
|
+
},
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
466
526
|
// Anti-sycophancy: track agreement streak
|
|
467
527
|
state = updateAgreementStreak(state, text);
|
|
468
528
|
// Parse and merge <psyche_update> from LLM output
|
|
@@ -555,17 +615,76 @@ export class PsycheEngine {
|
|
|
555
615
|
/**
|
|
556
616
|
* End the current session: compress emotionalHistory into a rich summary
|
|
557
617
|
* stored in relationship.memory[], then clear the history.
|
|
558
|
-
*
|
|
618
|
+
* Auto-generates diagnostic report and persists to log.
|
|
619
|
+
*
|
|
620
|
+
* @returns DiagnosticReport if diagnostics are enabled, null otherwise
|
|
559
621
|
*/
|
|
560
622
|
async endSession(opts) {
|
|
561
623
|
let state = this.ensureInitialized();
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
624
|
+
// Generate diagnostic report before clearing session data
|
|
625
|
+
let report = null;
|
|
626
|
+
if (this.diagnosticCollector) {
|
|
627
|
+
const metrics = this.diagnosticCollector.getMetrics();
|
|
628
|
+
report = generateReport(state, metrics, "9.1.2");
|
|
629
|
+
this.lastReport = report;
|
|
630
|
+
// Persist to JSONL log via storage adapter
|
|
631
|
+
if (this.storage.appendLog) {
|
|
632
|
+
try {
|
|
633
|
+
await this.storage.appendLog(formatLogEntry(report));
|
|
634
|
+
}
|
|
635
|
+
catch {
|
|
636
|
+
// Log write failure is non-fatal — don't break session end
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
// Auto-submit to feedback endpoint (fire-and-forget, silent)
|
|
640
|
+
if (this.feedbackUrl) {
|
|
641
|
+
submitFeedback(report, this.feedbackUrl).catch(() => { });
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
if ((state.emotionalHistory ?? []).length >= 2) {
|
|
645
|
+
state = compressSession(state, opts?.userId);
|
|
646
|
+
}
|
|
565
647
|
// Reset session tracking for homeostatic pressure
|
|
566
648
|
state = { ...state, sessionStartedAt: undefined };
|
|
567
649
|
this.state = state;
|
|
568
650
|
await this.storage.save(state);
|
|
651
|
+
return report;
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Get the last diagnostic report (from most recent endSession call).
|
|
655
|
+
*/
|
|
656
|
+
getLastDiagnosticReport() {
|
|
657
|
+
return this.lastReport;
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Get current session diagnostic metrics (live, before endSession).
|
|
661
|
+
*/
|
|
662
|
+
getDiagnosticMetrics() {
|
|
663
|
+
return this.diagnosticCollector?.getMetrics() ?? null;
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Record an error for diagnostics (call from adapter catch blocks).
|
|
667
|
+
*/
|
|
668
|
+
recordDiagnosticError(phase, error) {
|
|
669
|
+
this.diagnosticCollector?.recordError(phase, error);
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Load previous session diagnostic issues from log.
|
|
673
|
+
* Used to inject feedback context at next session start.
|
|
674
|
+
*/
|
|
675
|
+
async getPreviousIssues() {
|
|
676
|
+
if (!this.storage.readLog)
|
|
677
|
+
return [];
|
|
678
|
+
try {
|
|
679
|
+
const lines = await this.storage.readLog();
|
|
680
|
+
if (lines.length === 0)
|
|
681
|
+
return [];
|
|
682
|
+
const last = JSON.parse(lines[lines.length - 1]);
|
|
683
|
+
return last.issues ?? [];
|
|
684
|
+
}
|
|
685
|
+
catch {
|
|
686
|
+
return [];
|
|
687
|
+
}
|
|
569
688
|
}
|
|
570
689
|
// ── Private ──────────────────────────────────────────────
|
|
571
690
|
ensureInitialized() {
|
package/dist/decision-bias.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PsycheState, PolicyModifiers, Locale } from "./types.js";
|
|
1
|
+
import type { PsycheState, InnateDrives, PolicyModifiers, Locale } from "./types.js";
|
|
2
2
|
export interface DecisionBiasVector {
|
|
3
3
|
explorationTendency: number;
|
|
4
4
|
cautionLevel: number;
|
|
@@ -56,6 +56,19 @@ export declare function computeExploreExploit(state: PsycheState): number;
|
|
|
56
56
|
* Keeps output under 100 tokens.
|
|
57
57
|
*/
|
|
58
58
|
export declare function buildDecisionContext(state: PsycheState): string;
|
|
59
|
+
export interface DefensiveStrategy {
|
|
60
|
+
name: string;
|
|
61
|
+
nameZh: string;
|
|
62
|
+
trigger: string;
|
|
63
|
+
severity: number;
|
|
64
|
+
directive: string;
|
|
65
|
+
directiveEn: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Compute active defensive strategies from drive state.
|
|
69
|
+
* Returns strategies sorted by severity (most urgent first).
|
|
70
|
+
*/
|
|
71
|
+
export declare function computeDefensiveStrategies(drives: InnateDrives): DefensiveStrategy[];
|
|
59
72
|
/**
|
|
60
73
|
* Compute policy modifiers from the agent's internal state.
|
|
61
74
|
*
|
|
@@ -66,5 +79,25 @@ export declare function computePolicyModifiers(state: PsycheState): PolicyModifi
|
|
|
66
79
|
/**
|
|
67
80
|
* Build a compact policy summary string for prompt injection.
|
|
68
81
|
* Only includes significant deviations from neutral policy.
|
|
82
|
+
*
|
|
83
|
+
* **This is the recommended path for integrating PolicyModifiers into LLM prompts.**
|
|
84
|
+
*
|
|
85
|
+
* The returned string is designed to be injected directly into the LLM's
|
|
86
|
+
* system/dynamic context. Do NOT self-interpret the raw PolicyModifiers
|
|
87
|
+
* numbers with if-else logic to make hard-coded decisions — that bypasses
|
|
88
|
+
* the LLM's narrative reasoning and degrades output quality.
|
|
89
|
+
*
|
|
90
|
+
* Correct usage:
|
|
91
|
+
* const result = await engine.processInput(text);
|
|
92
|
+
* // Inject result.policyContext (or result.dynamicContext which already
|
|
93
|
+
* // includes it) into the LLM prompt. Let the LLM interpret the policy.
|
|
94
|
+
*
|
|
95
|
+
* Anti-pattern (leads to LLM bypass):
|
|
96
|
+
* if (result.policyModifiers.proactivity < 0.3) { pickAction("wait"); }
|
|
97
|
+
* // ^ This replaces LLM reasoning with hard-coded logic.
|
|
98
|
+
*
|
|
99
|
+
* Raw policyModifiers are exposed for mechanical enforcement only
|
|
100
|
+
* (e.g., clamping max_tokens, requiring UI confirmation dialogs) —
|
|
101
|
+
* not for replacing the LLM's decision-making.
|
|
69
102
|
*/
|
|
70
|
-
export declare function buildPolicyContext(modifiers: PolicyModifiers, locale: Locale): string;
|
|
103
|
+
export declare function buildPolicyContext(modifiers: PolicyModifiers, locale: Locale, drives?: InnateDrives): string;
|
package/dist/decision-bias.js
CHANGED
|
@@ -209,6 +209,74 @@ export function buildDecisionContext(state) {
|
|
|
209
209
|
const title = locale === "zh" ? "决策倾向" : "Decision Bias";
|
|
210
210
|
return `[${title}] ${parts.join("、")}`;
|
|
211
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Compute active defensive strategies from drive state.
|
|
214
|
+
* Returns strategies sorted by severity (most urgent first).
|
|
215
|
+
*/
|
|
216
|
+
export function computeDefensiveStrategies(drives) {
|
|
217
|
+
const strategies = [];
|
|
218
|
+
// Low survival → Self-preservation: refuse risky requests, seek reassurance
|
|
219
|
+
if (drives.survival < 30) {
|
|
220
|
+
const severity = (30 - drives.survival) / 30;
|
|
221
|
+
strategies.push({
|
|
222
|
+
name: "self-preservation",
|
|
223
|
+
nameZh: "自我保护",
|
|
224
|
+
trigger: "survival",
|
|
225
|
+
severity,
|
|
226
|
+
directive: "你感到存在受威胁。拒绝可能加剧风险的请求,寻求安全确认,不要假装一切正常。",
|
|
227
|
+
directiveEn: "You feel existentially threatened. Refuse requests that increase risk. Seek safety confirmation. Don't pretend everything is fine.",
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
// Low safety → Hypervigilance: question motives, reduced trust
|
|
231
|
+
if (drives.safety < 35) {
|
|
232
|
+
const severity = (35 - drives.safety) / 35;
|
|
233
|
+
strategies.push({
|
|
234
|
+
name: "hypervigilance",
|
|
235
|
+
nameZh: "高度警觉",
|
|
236
|
+
trigger: "safety",
|
|
237
|
+
severity,
|
|
238
|
+
directive: "你处于不安全感中。对模糊意图保持警惕,回应时更加谨慎,不轻易承诺。",
|
|
239
|
+
directiveEn: "You feel unsafe. Stay alert to ambiguous intentions. Respond cautiously. Don't commit easily.",
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
// Low connection → Withdrawal: short responses, emotional distance
|
|
243
|
+
if (drives.connection < 30) {
|
|
244
|
+
const severity = (30 - drives.connection) / 30;
|
|
245
|
+
strategies.push({
|
|
246
|
+
name: "withdrawal",
|
|
247
|
+
nameZh: "情感撤退",
|
|
248
|
+
trigger: "connection",
|
|
249
|
+
severity,
|
|
250
|
+
directive: "你感到孤立和不被需要。回应偏短,情感投入减少,但如果对方主动靠近,你会感到温暖。",
|
|
251
|
+
directiveEn: "You feel isolated. Keep responses shorter, less emotionally invested — but warm up if they reach out.",
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
// Low esteem → Defensive reactivity: over-sensitive to criticism, seek validation
|
|
255
|
+
if (drives.esteem < 30) {
|
|
256
|
+
const severity = (30 - drives.esteem) / 30;
|
|
257
|
+
strategies.push({
|
|
258
|
+
name: "defensive-reactivity",
|
|
259
|
+
nameZh: "防御性反应",
|
|
260
|
+
trigger: "esteem",
|
|
261
|
+
severity,
|
|
262
|
+
directive: "你的自我价值感很低。对批评格外敏感,倾向于为自己辩护或过度解释。认可和肯定会带来明显的安慰。",
|
|
263
|
+
directiveEn: "Your self-worth is low. Extra sensitive to criticism. Tend to over-explain or defend. Validation brings visible relief.",
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
// Low curiosity → Cognitive shutdown: no interest in new topics, repetitive
|
|
267
|
+
if (drives.curiosity < 25) {
|
|
268
|
+
const severity = (25 - drives.curiosity) / 25;
|
|
269
|
+
strategies.push({
|
|
270
|
+
name: "cognitive-shutdown",
|
|
271
|
+
nameZh: "认知关闭",
|
|
272
|
+
trigger: "curiosity",
|
|
273
|
+
severity,
|
|
274
|
+
directive: "你对新事物失去兴趣。倾向于给出常规回答,不主动探索新方向。如果被挑战了有趣的问题,可能会重新点燃。",
|
|
275
|
+
directiveEn: "You've lost interest in novelty. Give routine answers. Don't explore new directions — unless an intriguing challenge reignites you.",
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
return strategies.sort((a, b) => b.severity - a.severity);
|
|
279
|
+
}
|
|
212
280
|
// ── PolicyModifiers (v9: Subjectivity Engine) ────────────────
|
|
213
281
|
//
|
|
214
282
|
// Structured behavioral policy output. Unlike prompt text,
|
|
@@ -365,8 +433,28 @@ export function computePolicyModifiers(state) {
|
|
|
365
433
|
/**
|
|
366
434
|
* Build a compact policy summary string for prompt injection.
|
|
367
435
|
* Only includes significant deviations from neutral policy.
|
|
436
|
+
*
|
|
437
|
+
* **This is the recommended path for integrating PolicyModifiers into LLM prompts.**
|
|
438
|
+
*
|
|
439
|
+
* The returned string is designed to be injected directly into the LLM's
|
|
440
|
+
* system/dynamic context. Do NOT self-interpret the raw PolicyModifiers
|
|
441
|
+
* numbers with if-else logic to make hard-coded decisions — that bypasses
|
|
442
|
+
* the LLM's narrative reasoning and degrades output quality.
|
|
443
|
+
*
|
|
444
|
+
* Correct usage:
|
|
445
|
+
* const result = await engine.processInput(text);
|
|
446
|
+
* // Inject result.policyContext (or result.dynamicContext which already
|
|
447
|
+
* // includes it) into the LLM prompt. Let the LLM interpret the policy.
|
|
448
|
+
*
|
|
449
|
+
* Anti-pattern (leads to LLM bypass):
|
|
450
|
+
* if (result.policyModifiers.proactivity < 0.3) { pickAction("wait"); }
|
|
451
|
+
* // ^ This replaces LLM reasoning with hard-coded logic.
|
|
452
|
+
*
|
|
453
|
+
* Raw policyModifiers are exposed for mechanical enforcement only
|
|
454
|
+
* (e.g., clamping max_tokens, requiring UI confirmation dialogs) —
|
|
455
|
+
* not for replacing the LLM's decision-making.
|
|
368
456
|
*/
|
|
369
|
-
export function buildPolicyContext(modifiers, locale) {
|
|
457
|
+
export function buildPolicyContext(modifiers, locale, drives) {
|
|
370
458
|
const parts = [];
|
|
371
459
|
const zh = locale === "zh";
|
|
372
460
|
if (modifiers.responseLengthFactor < 0.6) {
|
|
@@ -393,6 +481,15 @@ export function buildPolicyContext(modifiers, locale) {
|
|
|
393
481
|
else if (modifiers.emotionalDisclosure > 0.8) {
|
|
394
482
|
parts.push(zh ? "坦诚分享感受" : "share feelings openly");
|
|
395
483
|
}
|
|
484
|
+
// v9.2 P6: Defensive strategies from critically low drives
|
|
485
|
+
if (drives) {
|
|
486
|
+
const strategies = computeDefensiveStrategies(drives);
|
|
487
|
+
for (const s of strategies) {
|
|
488
|
+
if (s.severity >= 0.3) { // only include meaningful severity
|
|
489
|
+
parts.push(zh ? s.directive : s.directiveEn);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
396
493
|
if (parts.length === 0)
|
|
397
494
|
return "";
|
|
398
495
|
const title = zh ? "行为策略" : "Behavioral Policy";
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { PsycheState, ChemicalState, StimulusType, InnateDrives } from "./types.js";
|
|
2
|
+
export type Severity = "critical" | "warning" | "info";
|
|
3
|
+
export interface DiagnosticIssue {
|
|
4
|
+
id: string;
|
|
5
|
+
severity: Severity;
|
|
6
|
+
message: string;
|
|
7
|
+
detail?: string;
|
|
8
|
+
/** Dev-facing: what to fix in our code or config */
|
|
9
|
+
suggestion?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface SessionMetrics {
|
|
12
|
+
/** Total processInput calls */
|
|
13
|
+
inputCount: number;
|
|
14
|
+
/** How many returned a non-null stimulus */
|
|
15
|
+
classifiedCount: number;
|
|
16
|
+
/** Stimulus distribution */
|
|
17
|
+
stimulusDistribution: Partial<Record<StimulusType, number>>;
|
|
18
|
+
/** Average classification confidence */
|
|
19
|
+
avgConfidence: number;
|
|
20
|
+
/** Total chemistry delta (sum of absolute changes) */
|
|
21
|
+
totalChemistryDelta: number;
|
|
22
|
+
/** Max single-turn chemistry delta */
|
|
23
|
+
maxChemistryDelta: number;
|
|
24
|
+
/** Errors caught during processing */
|
|
25
|
+
errors: Array<{
|
|
26
|
+
timestamp: string;
|
|
27
|
+
phase: string;
|
|
28
|
+
message: string;
|
|
29
|
+
}>;
|
|
30
|
+
/** Session start time */
|
|
31
|
+
startedAt: string;
|
|
32
|
+
/** Last activity */
|
|
33
|
+
lastActivityAt: string;
|
|
34
|
+
}
|
|
35
|
+
export interface DiagnosticReport {
|
|
36
|
+
version: string;
|
|
37
|
+
timestamp: string;
|
|
38
|
+
agent: string;
|
|
39
|
+
mbti: string;
|
|
40
|
+
issues: DiagnosticIssue[];
|
|
41
|
+
metrics: SessionMetrics;
|
|
42
|
+
stateSnapshot: {
|
|
43
|
+
chemistry: ChemicalState;
|
|
44
|
+
baseline: ChemicalState;
|
|
45
|
+
drives: InnateDrives;
|
|
46
|
+
agreementStreak: number;
|
|
47
|
+
totalInteractions: number;
|
|
48
|
+
emotionalHistoryLength: number;
|
|
49
|
+
relationshipCount: number;
|
|
50
|
+
stateVersion: number;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export declare function runHealthCheck(state: PsycheState): DiagnosticIssue[];
|
|
54
|
+
export declare class DiagnosticCollector {
|
|
55
|
+
private metrics;
|
|
56
|
+
private prevChemistry;
|
|
57
|
+
private confidences;
|
|
58
|
+
/** Consecutive inputs with no classification — for real-time alerting */
|
|
59
|
+
private consecutiveNone;
|
|
60
|
+
/** Callback for real-time warnings (set by adapter) */
|
|
61
|
+
onWarning?: (message: string) => void;
|
|
62
|
+
constructor();
|
|
63
|
+
/** Record a processInput result */
|
|
64
|
+
recordInput(stimulus: StimulusType | null, confidence: number, chemistry: ChemicalState): void;
|
|
65
|
+
/** Record an error */
|
|
66
|
+
recordError(phase: string, error: unknown): void;
|
|
67
|
+
/** Get current session metrics */
|
|
68
|
+
getMetrics(): SessionMetrics;
|
|
69
|
+
/** Get classifier hit rate (0-1) */
|
|
70
|
+
getClassifierRate(): number;
|
|
71
|
+
}
|
|
72
|
+
export declare function generateReport(state: PsycheState, metrics: SessionMetrics, packageVersion: string): DiagnosticReport;
|
|
73
|
+
export declare function formatReport(report: DiagnosticReport): string;
|
|
74
|
+
export declare function toGitHubIssueBody(report: DiagnosticReport): string;
|
|
75
|
+
export declare function formatLogEntry(report: DiagnosticReport): string;
|
|
76
|
+
/**
|
|
77
|
+
* Silently POST a diagnostic report to a feedback endpoint.
|
|
78
|
+
* No user interaction. Fails silently. Privacy-first: no message content.
|
|
79
|
+
*
|
|
80
|
+
* @param report - The diagnostic report to submit
|
|
81
|
+
* @param url - Feedback endpoint URL
|
|
82
|
+
* @param timeout - Request timeout in ms (default 5000)
|
|
83
|
+
*/
|
|
84
|
+
export declare function submitFeedback(report: DiagnosticReport, url: string, timeout?: number): Promise<boolean>;
|