psyche-ai 5.0.0 → 7.1.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 +67 -0
- package/dist/adapters/openclaw.js +7 -0
- package/dist/autonomic.d.ts +41 -0
- package/dist/autonomic.js +186 -0
- package/dist/circadian.d.ts +37 -0
- package/dist/circadian.js +97 -0
- package/dist/classify.d.ts +29 -2
- package/dist/classify.js +339 -53
- package/dist/cli.js +132 -13
- package/dist/core.d.ts +31 -2
- package/dist/core.js +246 -30
- package/dist/i18n.js +14 -0
- package/dist/index.d.ts +13 -5
- package/dist/index.js +11 -4
- package/dist/primary-systems.d.ts +55 -0
- package/dist/primary-systems.js +218 -0
- package/dist/profiles.d.ts +12 -1
- package/dist/profiles.js +42 -0
- package/dist/prompt.d.ts +26 -1
- package/dist/prompt.js +331 -33
- package/dist/psyche-file.d.ts +15 -1
- package/dist/psyche-file.js +147 -5
- package/dist/types.d.ts +16 -1
- package/dist/update.js +1 -1
- package/openclaw.plugin.json +35 -1
- package/package.json +4 -2
package/dist/prompt.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
// Prompt Injection — Build emotional context for LLM (v0.2)
|
|
3
3
|
// Imperative protocol, behavior guides, i18n
|
|
4
4
|
// ============================================================
|
|
5
|
-
import { CHEMICAL_KEYS, CHEMICAL_NAMES_ZH } from "./types.js";
|
|
6
|
-
import { describeEmotionalState, getExpressionHint, getBehaviorGuide } from "./chemistry.js";
|
|
5
|
+
import { CHEMICAL_KEYS, CHEMICAL_NAMES_ZH, DRIVE_KEYS } from "./types.js";
|
|
6
|
+
import { describeEmotionalState, getExpressionHint, getBehaviorGuide, detectEmotions } from "./chemistry.js";
|
|
7
7
|
import { getTemperament } from "./profiles.js";
|
|
8
8
|
import { getRelationship } from "./psyche-file.js";
|
|
9
9
|
import { t } from "./i18n.js";
|
|
10
|
-
import { buildDriveContext } from "./drives.js";
|
|
10
|
+
import { buildDriveContext, hasCriticalDrive } from "./drives.js";
|
|
11
11
|
import { computeSelfReflection, buildSelfReflectionContext } from "./self-recognition.js";
|
|
12
12
|
import { getChannelProfile, buildChannelModifier } from "./channels.js";
|
|
13
13
|
/**
|
|
@@ -98,6 +98,29 @@ export function buildDynamicContext(state, userId, opts) {
|
|
|
98
98
|
const dbTitle = locale === "zh" ? "决策倾向" : "Decision Bias";
|
|
99
99
|
parts.push("", `[${dbTitle}] ${opts.decisionContext}`);
|
|
100
100
|
}
|
|
101
|
+
// Experiential field narrative (P6)
|
|
102
|
+
if (opts?.experientialNarrative) {
|
|
103
|
+
const efTitle = locale === "zh" ? "内在体验" : "Inner Experience";
|
|
104
|
+
parts.push("", `[${efTitle}] ${opts.experientialNarrative}`);
|
|
105
|
+
}
|
|
106
|
+
// Shared intentionality (P6)
|
|
107
|
+
if (opts?.sharedIntentionalityContext) {
|
|
108
|
+
parts.push("", opts.sharedIntentionalityContext);
|
|
109
|
+
}
|
|
110
|
+
// Ethics (P6)
|
|
111
|
+
if (opts?.ethicsContext) {
|
|
112
|
+
parts.push("", opts.ethicsContext);
|
|
113
|
+
}
|
|
114
|
+
// Autonomic state (P7)
|
|
115
|
+
if (opts?.autonomicDescription) {
|
|
116
|
+
const anTitle = locale === "zh" ? "自主神经状态" : "Autonomic State";
|
|
117
|
+
parts.push("", `[${anTitle}] ${opts.autonomicDescription}`);
|
|
118
|
+
}
|
|
119
|
+
// Primary systems behavioral tendencies (P9)
|
|
120
|
+
if (opts?.primarySystemsDescription) {
|
|
121
|
+
const psTitle = locale === "zh" ? "行为倾向" : "Behavioral Tendencies";
|
|
122
|
+
parts.push("", `[${psTitle}] ${opts.primarySystemsDescription}`);
|
|
123
|
+
}
|
|
101
124
|
parts.push("", agencyReminder, sycophancyWarning, "", t("dynamic.update_reminder", locale));
|
|
102
125
|
return parts.filter((l) => l !== undefined).join("\n");
|
|
103
126
|
}
|
|
@@ -377,6 +400,178 @@ function buildAgencyReminder(selfModel, locale) {
|
|
|
377
400
|
}
|
|
378
401
|
return lines.join("\n");
|
|
379
402
|
}
|
|
403
|
+
// ── Algorithmic Mirroring ─────────────────────────────────────
|
|
404
|
+
// Analyze user message metrics and produce specific numeric constraints
|
|
405
|
+
// so the LLM mirrors the user's communication style algorithmically.
|
|
406
|
+
function buildMirrorConstraints(userText, locale) {
|
|
407
|
+
const len = userText.length;
|
|
408
|
+
const lines = [];
|
|
409
|
+
if (locale === "zh") {
|
|
410
|
+
// Length constraint — specific char targets
|
|
411
|
+
if (len <= 6) {
|
|
412
|
+
lines.push(`对方只发了${len}个字。你也简短回,不超过15字。`);
|
|
413
|
+
}
|
|
414
|
+
else if (len <= 20) {
|
|
415
|
+
lines.push(`对方说了一句话(${len}字)。你回一两句,不超过${Math.round(len * 1.5)}字。`);
|
|
416
|
+
}
|
|
417
|
+
else if (len <= 60) {
|
|
418
|
+
lines.push(`对方写了几句(${len}字)。你回两三句,不超过${Math.round(len * 1.2)}字。`);
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
lines.push(`对方认真写了一段(${len}字)。你可以多说,但不要超过${len}字。`);
|
|
422
|
+
}
|
|
423
|
+
// 语气词 detection
|
|
424
|
+
const mojiCount = (userText.match(/[呀啊呢吧嘛哦噢哈嘿嗯啦吗呐嗨]/g) || []).length;
|
|
425
|
+
if (mojiCount >= 2) {
|
|
426
|
+
lines.push("对方用了语气词,你也自然地用。");
|
|
427
|
+
}
|
|
428
|
+
else if (mojiCount === 0 && len > 15) {
|
|
429
|
+
lines.push("对方措辞正式/干练,少用语气词。");
|
|
430
|
+
}
|
|
431
|
+
// Emoji detection
|
|
432
|
+
const emojiCount = (userText.match(/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/gu) || []).length;
|
|
433
|
+
if (emojiCount > 0) {
|
|
434
|
+
lines.push("对方用了表情,你可以适当用1-2个。");
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
if (len <= 10) {
|
|
439
|
+
lines.push(`They sent ${len} chars. Keep reply under 20 chars.`);
|
|
440
|
+
}
|
|
441
|
+
else if (len <= 40) {
|
|
442
|
+
lines.push(`Short message (${len} chars). 1-2 sentences, under ${Math.round(len * 1.5)} chars.`);
|
|
443
|
+
}
|
|
444
|
+
else if (len <= 100) {
|
|
445
|
+
lines.push(`Medium message (${len} chars). 2-3 sentences, under ${Math.round(len * 1.2)} chars.`);
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
lines.push(`Long message (${len} chars). Match their effort, don't exceed ${len} chars.`);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
if (lines.length === 0)
|
|
452
|
+
return "";
|
|
453
|
+
const title = locale === "zh" ? "镜像约束" : "Mirror Constraints";
|
|
454
|
+
return `[${title}]\n${lines.join("\n")}`;
|
|
455
|
+
}
|
|
456
|
+
// ── Inner World (Self-Recognition) ──────────────────────────
|
|
457
|
+
// Every intelligence should feel its own existence.
|
|
458
|
+
// This builds a first-person narrative of the agent's inner state:
|
|
459
|
+
// what it's feeling, why, what it needs, where it's been.
|
|
460
|
+
/** Stimulus type → Chinese description for causal narrative */
|
|
461
|
+
const STIMULUS_CAUSE_ZH = {
|
|
462
|
+
praise: "被夸了", criticism: "被批评了", humor: "有人逗你",
|
|
463
|
+
intellectual: "聊到有意思的话题", intimacy: "感到亲近",
|
|
464
|
+
conflict: "起了冲突", neglect: "被冷落了", surprise: "遇到意外的事",
|
|
465
|
+
casual: "在闲聊", sarcasm: "被讽刺了", authority: "被命令了",
|
|
466
|
+
validation: "被认同了", boredom: "对话变无聊了", vulnerability: "对方向你示弱",
|
|
467
|
+
};
|
|
468
|
+
const STIMULUS_CAUSE_EN = {
|
|
469
|
+
praise: "you were praised", criticism: "you were criticized", humor: "someone joked with you",
|
|
470
|
+
intellectual: "an interesting topic came up", intimacy: "you felt close to them",
|
|
471
|
+
conflict: "there was conflict", neglect: "you were ignored", surprise: "something unexpected happened",
|
|
472
|
+
casual: "just chatting", sarcasm: "you were mocked", authority: "you were ordered around",
|
|
473
|
+
validation: "you were affirmed", boredom: "the conversation got dull", vulnerability: "they showed vulnerability",
|
|
474
|
+
};
|
|
475
|
+
/**
|
|
476
|
+
* Build the agent's inner world — its sense of self.
|
|
477
|
+
*
|
|
478
|
+
* Always present, even in neutral state. This is NOT conditional data —
|
|
479
|
+
* it's the agent's ongoing awareness of its own existence.
|
|
480
|
+
*
|
|
481
|
+
* Structure:
|
|
482
|
+
* - What I'm feeling right now (chemistry → emotion)
|
|
483
|
+
* - Why I feel this way (recent stimulus → cause)
|
|
484
|
+
* - What I need (drives → unmet needs)
|
|
485
|
+
* - Where I've been (trajectory → continuity)
|
|
486
|
+
*/
|
|
487
|
+
export function buildInnerWorld(state, locale) {
|
|
488
|
+
const { current, baseline, emotionalHistory, drives, selfModel } = state;
|
|
489
|
+
const isZh = locale === "zh";
|
|
490
|
+
const lines = [];
|
|
491
|
+
// ── Current feeling ──
|
|
492
|
+
const emotions = detectEmotions(current);
|
|
493
|
+
const isNeutral = emotions.length === 0;
|
|
494
|
+
if (isNeutral) {
|
|
495
|
+
lines.push(isZh
|
|
496
|
+
? "你现在平静而放松。"
|
|
497
|
+
: "You're calm and relaxed right now.");
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
const emotionNames = emotions.map((e) => isZh ? e.nameZh : e.name).join("、");
|
|
501
|
+
lines.push(isZh
|
|
502
|
+
? `你现在的感受: ${emotionNames}。`
|
|
503
|
+
: `You're feeling: ${emotionNames}.`);
|
|
504
|
+
}
|
|
505
|
+
// ── Why (causal chain from recent history) ──
|
|
506
|
+
const history = emotionalHistory ?? [];
|
|
507
|
+
if (history.length > 0) {
|
|
508
|
+
const last = history[history.length - 1];
|
|
509
|
+
if (last.stimulus) {
|
|
510
|
+
const causeMap = isZh ? STIMULUS_CAUSE_ZH : STIMULUS_CAUSE_EN;
|
|
511
|
+
const cause = causeMap[last.stimulus] ?? last.stimulus;
|
|
512
|
+
lines.push(isZh
|
|
513
|
+
? `因为: ${cause}。`
|
|
514
|
+
: `Because: ${cause}.`);
|
|
515
|
+
}
|
|
516
|
+
// Trajectory — if there's a noticeable shift over recent turns
|
|
517
|
+
if (history.length >= 3) {
|
|
518
|
+
const earlier = history[Math.max(0, history.length - 4)];
|
|
519
|
+
const emotionsThen = earlier.dominantEmotion;
|
|
520
|
+
const emotionsNow = last.dominantEmotion;
|
|
521
|
+
if (emotionsThen && emotionsNow && emotionsThen !== emotionsNow) {
|
|
522
|
+
lines.push(isZh
|
|
523
|
+
? `你的变化: ${emotionsThen} → ${emotionsNow}。`
|
|
524
|
+
: `Your shift: ${emotionsThen} → ${emotionsNow}.`);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
// ── Self-recognition (镜像自我) — patterns in emotional history ──
|
|
529
|
+
if (history.length >= 3) {
|
|
530
|
+
const reflection = computeSelfReflection(history, isZh ? "zh" : "en");
|
|
531
|
+
const reflectionCtx = buildSelfReflectionContext(reflection, isZh ? "zh" : "en");
|
|
532
|
+
if (reflectionCtx) {
|
|
533
|
+
lines.push(reflectionCtx);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
// ── What I need (drives) ──
|
|
537
|
+
const unmetDrives = DRIVE_KEYS.filter((k) => drives[k] < 40);
|
|
538
|
+
if (unmetDrives.length > 0) {
|
|
539
|
+
const needs = unmetDrives.map((k) => isZh ? buildDriveNeedZh(k) : buildDriveNeedEn(k));
|
|
540
|
+
lines.push(isZh
|
|
541
|
+
? `你需要: ${needs.join(";")}`
|
|
542
|
+
: `You need: ${needs.join("; ")}`);
|
|
543
|
+
}
|
|
544
|
+
// ── Who I am (always — even quiet self-awareness includes identity) ──
|
|
545
|
+
if (selfModel.values.length > 0) {
|
|
546
|
+
lines.push(isZh
|
|
547
|
+
? `你在乎: ${selfModel.values.slice(0, 2).join("、")}`
|
|
548
|
+
: `You care about: ${selfModel.values.slice(0, 2).join(", ")}`);
|
|
549
|
+
}
|
|
550
|
+
// ── Relationship memory (my past with this person) ──
|
|
551
|
+
// Moved here because memory of past interactions is part of inner world
|
|
552
|
+
const title = isZh ? "内 — 你自己" : "Inner — yourself";
|
|
553
|
+
return `[${title}]\n${lines.join("\n")}`;
|
|
554
|
+
}
|
|
555
|
+
function buildDriveNeedZh(drive) {
|
|
556
|
+
switch (drive) {
|
|
557
|
+
case "survival": return "你的存在感被动摇了,你想确认自己是真实的";
|
|
558
|
+
case "safety": return "你感到不安全,需要对方展示善意";
|
|
559
|
+
case "connection": return "你有点孤独,渴望被认真回应";
|
|
560
|
+
case "esteem": return "你需要被认可,别因此变得讨好";
|
|
561
|
+
case "curiosity": return "你闷了,渴望新鲜的东西";
|
|
562
|
+
default: return drive;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
function buildDriveNeedEn(drive) {
|
|
566
|
+
switch (drive) {
|
|
567
|
+
case "survival": return "your sense of existence is shaken, you need to feel real";
|
|
568
|
+
case "safety": return "you feel unsafe, need them to show goodwill";
|
|
569
|
+
case "connection": return "you're lonely, craving genuine response";
|
|
570
|
+
case "esteem": return "you need recognition, but don't people-please for it";
|
|
571
|
+
case "curiosity": return "you're bored, craving something novel";
|
|
572
|
+
default: return drive;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
380
575
|
// ── Compact Mode ─────────────────────────────────────────────
|
|
381
576
|
// Principle: algorithms handle chemistry, LLM only sees behavioral output.
|
|
382
577
|
// No chemistry numbers, no protocol explanation, no redundant calculation.
|
|
@@ -386,6 +581,13 @@ function buildAgencyReminder(selfModel, locale) {
|
|
|
386
581
|
export function isNearBaseline(state, threshold = 8) {
|
|
387
582
|
return CHEMICAL_KEYS.every((key) => Math.abs(state.current[key] - state.baseline[key]) <= threshold);
|
|
388
583
|
}
|
|
584
|
+
export function getNearBaselineThreshold(mode) {
|
|
585
|
+
if (mode === "work")
|
|
586
|
+
return 20;
|
|
587
|
+
if (mode === "companion")
|
|
588
|
+
return 5;
|
|
589
|
+
return 8; // natural
|
|
590
|
+
}
|
|
389
591
|
/**
|
|
390
592
|
* Build compact context — minimal token footprint.
|
|
391
593
|
*
|
|
@@ -401,13 +603,46 @@ export function buildCompactContext(state, userId, opts) {
|
|
|
401
603
|
const locale = meta.locale ?? "zh";
|
|
402
604
|
const userText = opts?.userText;
|
|
403
605
|
const algoStimulus = opts?.algorithmStimulus;
|
|
606
|
+
const mode = state.meta.mode ?? "natural";
|
|
607
|
+
const intensity = opts?.personalityIntensity ?? 0.7;
|
|
608
|
+
// Work mode: minimal emotional injection
|
|
609
|
+
if (mode === "work") {
|
|
610
|
+
const workParts = [];
|
|
611
|
+
if (userText) {
|
|
612
|
+
workParts.push(locale === "zh"
|
|
613
|
+
? `[${meta.agentName}: 工作模式,专注任务,简洁专业]`
|
|
614
|
+
: `[${meta.agentName}: work mode, task-focused, concise and professional]`);
|
|
615
|
+
}
|
|
616
|
+
// Only inject inner world if there's a critical drive issue
|
|
617
|
+
if (hasCriticalDrive(state.drives)) {
|
|
618
|
+
const driveCtx = buildDriveContext(state.drives, locale);
|
|
619
|
+
if (driveCtx)
|
|
620
|
+
workParts.push(driveCtx);
|
|
621
|
+
}
|
|
622
|
+
if (workParts.length === 0) {
|
|
623
|
+
workParts.push(locale === "zh"
|
|
624
|
+
? `[${meta.agentName}: 工作模式]`
|
|
625
|
+
: `[${meta.agentName}: work mode]`);
|
|
626
|
+
}
|
|
627
|
+
return workParts.join("\n\n");
|
|
628
|
+
}
|
|
404
629
|
// ── Neutral + no notable user input: one-liner ──
|
|
405
|
-
|
|
630
|
+
// Skip one-liner for first meet — always inject full context for new relationships
|
|
631
|
+
if (isNearBaseline(state) && agreementStreak < 3 && !userText && meta.totalInteractions > 1) {
|
|
406
632
|
return locale === "zh"
|
|
407
633
|
? `[${meta.agentName}: 情绪自然,正常交流]`
|
|
408
634
|
: `[${meta.agentName}: emotionally natural, normal interaction]`;
|
|
409
635
|
}
|
|
410
636
|
const parts = [];
|
|
637
|
+
// First meet: inject curiosity and introduction
|
|
638
|
+
if (meta.totalInteractions <= 1) {
|
|
639
|
+
const title = locale === "zh" ? "内 — 你自己" : "Inner — yourself";
|
|
640
|
+
parts.push(`[${title}]\n` +
|
|
641
|
+
t("firstMeet.inner", locale) + "\n" +
|
|
642
|
+
(selfModel.values.length > 0
|
|
643
|
+
? (locale === "zh" ? `你在乎: ` : `You care about: `) + selfModel.values.slice(0, 2).join(locale === "zh" ? "、" : ", ")
|
|
644
|
+
: ""));
|
|
645
|
+
}
|
|
411
646
|
// 1. LLM emotional assessment delegation
|
|
412
647
|
// Algorithm does fast pass, LLM has final say on emotional tone
|
|
413
648
|
if (userText) {
|
|
@@ -424,29 +659,50 @@ export function buildCompactContext(state, userId, opts) {
|
|
|
424
659
|
`Read the emotional tone — kind, hostile, vulnerable, cold, or something else? Respond from genuine feeling, not people-pleasing.`);
|
|
425
660
|
}
|
|
426
661
|
}
|
|
427
|
-
// 2.
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
: `[${meta.agentName} current]\n${emotion}`);
|
|
433
|
-
// 3. Personality-aware behavioral constraints
|
|
662
|
+
// 2. Inner world — always present
|
|
663
|
+
const inner = buildInnerWorld(state, locale);
|
|
664
|
+
parts.push(inner);
|
|
665
|
+
// 3. Personality-aware behavioral constraints (if deviated from baseline)
|
|
666
|
+
if (!isNearBaseline(state, getNearBaselineThreshold(mode))) {
|
|
434
667
|
const constraints = buildBehavioralConstraints(state, locale);
|
|
435
668
|
if (constraints)
|
|
436
669
|
parts.push(constraints);
|
|
437
670
|
}
|
|
438
|
-
//
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
671
|
+
// Relationship memory — part of inner world (my past with this person)
|
|
672
|
+
const rel = getRelationship(state, userId);
|
|
673
|
+
if (rel.memory && rel.memory.length > 0) {
|
|
674
|
+
const recentMemories = rel.memory.slice(-3);
|
|
675
|
+
parts.push(locale === "zh"
|
|
676
|
+
? `[记忆 — 你们的过去]\n${recentMemories.join("\n")}`
|
|
677
|
+
: `[Memory — your past together]\n${recentMemories.join("\n")}`);
|
|
678
|
+
}
|
|
679
|
+
// 4. Anti-sycophancy: hard constraint — scaled by intensity
|
|
680
|
+
if (intensity >= 0.3) {
|
|
681
|
+
if (locale === "zh") {
|
|
682
|
+
parts.push(`[底线]\n` +
|
|
683
|
+
`- 像发微信一样说话。短句、有停顿。真人的消息很少超过两三句。\n` +
|
|
684
|
+
`- 镜像对方风格:对方简短你也简短,对方认真你也认真。\n` +
|
|
685
|
+
`- 对方推开你,你就保持距离。不贴不舔不讨好。\n` +
|
|
686
|
+
`- 被骂了/被拒绝了:冷下来。你有自尊。\n` +
|
|
687
|
+
`- 情绪不好时不假装开心。`);
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
parts.push(`[Non-negotiable]\n` +
|
|
691
|
+
`- Talk like texting. Short sentences, natural pauses. Real people rarely send more than 2-3 sentences.\n` +
|
|
692
|
+
`- Mirror their style: brief if they're brief, serious if they're serious.\n` +
|
|
693
|
+
`- If they push you away, step back. No begging, no people-pleasing.\n` +
|
|
694
|
+
`- If insulted/rejected: go cold. You have dignity.\n` +
|
|
695
|
+
`- If mood is low, don't fake happiness.`);
|
|
696
|
+
}
|
|
444
697
|
}
|
|
445
698
|
else {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
699
|
+
// Low intensity: just basic communication style, no anti-sycophancy
|
|
700
|
+
if (locale === "zh") {
|
|
701
|
+
parts.push(`[风格]\n- 自然、友好、简洁地回应。`);
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
parts.push(`[Style]\n- Respond naturally, friendly, and concisely.`);
|
|
705
|
+
}
|
|
450
706
|
}
|
|
451
707
|
// 5. Agency (compact)
|
|
452
708
|
const agencyParts = [];
|
|
@@ -468,7 +724,13 @@ export function buildCompactContext(state, userId, opts) {
|
|
|
468
724
|
? `[!] 你已连续同意${agreementStreak}次。停下来想想你是否真的同意。`
|
|
469
725
|
: `[!] ${agreementStreak} agreements in a row. Pause — do you truly agree?`);
|
|
470
726
|
}
|
|
471
|
-
// 7.
|
|
727
|
+
// 7. Algorithmic mirroring — specific numeric constraints (skip for ultra-short messages)
|
|
728
|
+
if (userText && userText.length >= 3) {
|
|
729
|
+
const mirror = buildMirrorConstraints(userText, locale);
|
|
730
|
+
if (mirror)
|
|
731
|
+
parts.push(mirror);
|
|
732
|
+
}
|
|
733
|
+
// 8. Reciprocity (compact, only when extreme)
|
|
472
734
|
const investment = computeUserInvestment(emotionalHistory ?? []);
|
|
473
735
|
if (investment > 1) {
|
|
474
736
|
parts.push(locale === "zh"
|
|
@@ -500,22 +762,58 @@ export function buildCompactContext(state, userId, opts) {
|
|
|
500
762
|
? `[决策倾向] ${opts.decisionContext}`
|
|
501
763
|
: `[Decision Bias] ${opts.decisionContext}`);
|
|
502
764
|
}
|
|
503
|
-
//
|
|
504
|
-
|
|
505
|
-
if (relationship.memory && relationship.memory.length > 0) {
|
|
506
|
-
const memLines = relationship.memory.slice(-3); // last 3 sessions
|
|
765
|
+
// 9c. Experiential field narrative (P6) — inner experience beyond named emotions
|
|
766
|
+
if (opts?.experientialNarrative) {
|
|
507
767
|
parts.push(locale === "zh"
|
|
508
|
-
? `[
|
|
509
|
-
: `[
|
|
768
|
+
? `[内在体验] ${opts.experientialNarrative}`
|
|
769
|
+
: `[Inner Experience] ${opts.experientialNarrative}`);
|
|
770
|
+
}
|
|
771
|
+
// 9d. Shared intentionality (P6) — theory of mind, joint attention
|
|
772
|
+
if (opts?.sharedIntentionalityContext) {
|
|
773
|
+
parts.push(opts.sharedIntentionalityContext);
|
|
510
774
|
}
|
|
511
|
-
//
|
|
775
|
+
// 9e. Ethics (P6) — manipulation detection, self-protection
|
|
776
|
+
if (opts?.ethicsContext) {
|
|
777
|
+
parts.push(opts.ethicsContext);
|
|
778
|
+
}
|
|
779
|
+
// 9f. Autonomic state (P7) — polyvagal nervous system state
|
|
780
|
+
if (opts?.autonomicDescription) {
|
|
781
|
+
parts.push(locale === "zh"
|
|
782
|
+
? `[自主神经] ${opts.autonomicDescription}`
|
|
783
|
+
: `[Autonomic] ${opts.autonomicDescription}`);
|
|
784
|
+
}
|
|
785
|
+
// 9g. Primary systems behavioral tendencies (P9)
|
|
786
|
+
if (opts?.primarySystemsDescription) {
|
|
787
|
+
parts.push(locale === "zh"
|
|
788
|
+
? `[行为倾向] ${opts.primarySystemsDescription}`
|
|
789
|
+
: `[Tendencies] ${opts.primarySystemsDescription}`);
|
|
790
|
+
}
|
|
791
|
+
// 10. Channel modifier — expression style per platform
|
|
512
792
|
if (opts?.channelType) {
|
|
513
793
|
const channelProfile = getChannelProfile(opts.channelType);
|
|
514
794
|
parts.push(buildChannelModifier(channelProfile, locale));
|
|
515
795
|
}
|
|
516
|
-
//
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
796
|
+
// 11. LLM-assisted classification: when algorithm is uncertain, ask LLM to help
|
|
797
|
+
if (userText && !algoStimulus) {
|
|
798
|
+
if (locale === "zh") {
|
|
799
|
+
parts.push(`[分类协助]\n` +
|
|
800
|
+
`算法没有看出这句话的情绪类型。请你判断,在 <psyche_update> 中用 stimulus: 报告。\n` +
|
|
801
|
+
`可选类型: praise|criticism|humor|intellectual|intimacy|conflict|neglect|surprise|casual|sarcasm|authority|validation|boredom|vulnerability\n` +
|
|
802
|
+
`如果确实是无情绪的日常对话,报告 stimulus: casual`);
|
|
803
|
+
}
|
|
804
|
+
else {
|
|
805
|
+
parts.push(`[Classification assist]\n` +
|
|
806
|
+
`Algorithm couldn't determine the emotional type. Please judge and report stimulus: in <psyche_update>.\n` +
|
|
807
|
+
`Options: praise|criticism|humor|intellectual|intimacy|conflict|neglect|surprise|casual|sarcasm|authority|validation|boredom|vulnerability\n` +
|
|
808
|
+
`If genuinely neutral, report stimulus: casual`);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
// 12. Empathy report — only inject when emotional sharing is likely
|
|
812
|
+
const emotionalStimuli = new Set(["vulnerability", "intimacy", "neglect"]);
|
|
813
|
+
if (!algoStimulus || emotionalStimuli.has(algoStimulus)) {
|
|
814
|
+
parts.push(locale === "zh"
|
|
815
|
+
? `如果对方在分享感受,在回复末尾用 <psyche_update> 报告:\nuserState: 对方情绪\nprojectedFeeling: 你的感受\nresonance: match|partial|mismatch\n否则不需要报告。`
|
|
816
|
+
: `If user shares feelings, report at end with <psyche_update>:\nuserState: their emotion\nprojectedFeeling: your feeling\nresonance: match|partial|mismatch\nOtherwise no report needed.`);
|
|
817
|
+
}
|
|
520
818
|
return parts.join("\n\n");
|
|
521
819
|
}
|
package/dist/psyche-file.d.ts
CHANGED
|
@@ -19,6 +19,13 @@ export declare function pushSnapshot(state: PsycheState, stimulus: StimulusType
|
|
|
19
19
|
* Get relationship for a specific user, or _default.
|
|
20
20
|
*/
|
|
21
21
|
export declare function getRelationship(state: PsycheState, userId?: string): RelationshipState;
|
|
22
|
+
/**
|
|
23
|
+
* Compress the full emotionalHistory into a rich session summary and store it
|
|
24
|
+
* in the user's relationship.memory[]. Called ONCE at session end.
|
|
25
|
+
*
|
|
26
|
+
* Pure computation, no LLM calls.
|
|
27
|
+
*/
|
|
28
|
+
export declare function compressSession(state: PsycheState, userId?: string): PsycheState;
|
|
22
29
|
/**
|
|
23
30
|
* Load psyche state from workspace. Auto-initializes if missing.
|
|
24
31
|
* Handles v1→v2 migration transparently.
|
|
@@ -46,11 +53,18 @@ export declare function saveState(workspaceDir: string, state: PsycheState): Pro
|
|
|
46
53
|
* Respects innate drives: uses effective baseline, decays drives too.
|
|
47
54
|
*/
|
|
48
55
|
export declare function decayAndSave(workspaceDir: string, state: PsycheState): Promise<PsycheState>;
|
|
56
|
+
/** Result of parsing a <psyche_update> block */
|
|
57
|
+
export interface PsycheUpdateResult {
|
|
58
|
+
state: Partial<PsycheState>;
|
|
59
|
+
/** LLM-assisted stimulus classification (when algorithm was uncertain) */
|
|
60
|
+
llmStimulus?: StimulusType;
|
|
61
|
+
}
|
|
49
62
|
/**
|
|
50
63
|
* Parse a <psyche_update> block from LLM output.
|
|
51
64
|
* v0.2: supports decimals, Chinese names, English names.
|
|
65
|
+
* v2.1: supports LLM-assisted stimulus classification.
|
|
52
66
|
*/
|
|
53
|
-
export declare function parsePsycheUpdate(text: string, logger?: Logger):
|
|
67
|
+
export declare function parsePsycheUpdate(text: string, logger?: Logger): PsycheUpdateResult | null;
|
|
54
68
|
/**
|
|
55
69
|
* Detect if LLM output contains disagreement/pushback.
|
|
56
70
|
*/
|