psyche-ai 9.2.3 → 9.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +8 -175
- package/README.md +33 -16
- package/dist/adapters/http.js +1 -1
- package/dist/adapters/langchain.d.ts +14 -0
- package/dist/adapters/langchain.js +20 -0
- package/dist/adapters/mcp.js +5 -1
- package/dist/adapters/openclaw.d.ts +1 -0
- package/dist/adapters/openclaw.js +67 -15
- package/dist/adapters/vercel-ai.d.ts +2 -1
- package/dist/adapters/vercel-ai.js +8 -9
- package/dist/appraisal.d.ts +8 -0
- package/dist/appraisal.js +362 -0
- package/dist/autonomic.js +2 -2
- package/dist/classify.js +14 -3
- package/dist/cli.js +28 -3
- package/dist/core.d.ts +9 -3
- package/dist/core.js +194 -12
- package/dist/demo.js +1 -2
- package/dist/diagnostics.d.ts +8 -6
- package/dist/diagnostics.js +53 -17
- package/dist/ethics.js +1 -1
- package/dist/experiential-field.d.ts +2 -2
- package/dist/experiential-field.js +7 -16
- package/dist/generative-self.js +0 -2
- package/dist/host-controls.d.ts +5 -0
- package/dist/host-controls.js +48 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.js +7 -1
- package/dist/interaction.js +0 -2
- package/dist/metacognition.d.ts +13 -1
- package/dist/metacognition.js +164 -32
- package/dist/prompt.d.ts +4 -0
- package/dist/prompt.js +67 -31
- package/dist/psyche-file.d.ts +16 -1
- package/dist/psyche-file.js +103 -8
- package/dist/relation-dynamics.d.ts +21 -0
- package/dist/relation-dynamics.js +601 -0
- package/dist/response-contract.d.ts +8 -0
- package/dist/response-contract.js +374 -0
- package/dist/storage.d.ts +1 -0
- package/dist/storage.js +12 -5
- package/dist/subjectivity.d.ts +3 -0
- package/dist/subjectivity.js +477 -0
- package/dist/temporal.d.ts +2 -2
- package/dist/temporal.js +2 -2
- package/dist/types.d.ts +243 -0
- package/dist/types.js +43 -0
- package/dist/update.d.ts +37 -2
- package/dist/update.js +323 -44
- package/openclaw.plugin.json +20 -1
- package/package.json +1 -1
package/dist/prompt.js
CHANGED
|
@@ -177,7 +177,6 @@ ${t("protocol.empathy", locale)}`;
|
|
|
177
177
|
function isFeeler(mbti) { return mbti[2] === "F"; }
|
|
178
178
|
function isExtravert(mbti) { return mbti[0] === "E"; }
|
|
179
179
|
function isPerceiver(mbti) { return mbti[3] === "P"; }
|
|
180
|
-
function isIntuitive(mbti) { return mbti[1] === "N"; }
|
|
181
180
|
/**
|
|
182
181
|
* Direction 3: Build personality-aware behavioral constraints from chemistry.
|
|
183
182
|
*
|
|
@@ -466,6 +465,27 @@ function buildMirrorConstraints(userText, locale) {
|
|
|
466
465
|
const title = locale === "zh" ? "镜像约束" : "Mirror Constraints";
|
|
467
466
|
return `[${title}]\n${lines.join("\n")}`;
|
|
468
467
|
}
|
|
468
|
+
function buildCompactSensingContext(_userText, locale, algorithmStimulus) {
|
|
469
|
+
if (locale === "zh") {
|
|
470
|
+
return algorithmStimulus
|
|
471
|
+
? `[情绪感知] 算法初判:${algorithmStimulus},仅供参考;按真实感受终判语气。`
|
|
472
|
+
: `[情绪感知] 这句的情绪基调由你终判。`;
|
|
473
|
+
}
|
|
474
|
+
return algorithmStimulus
|
|
475
|
+
? `[Emotional Sensing] Algorithm hint: ${algorithmStimulus}; final tonal read is yours.`
|
|
476
|
+
: `[Emotional Sensing] Make the final tonal read yourself.`;
|
|
477
|
+
}
|
|
478
|
+
function isNeutralMetacognitiveNote(note) {
|
|
479
|
+
return note.includes("normal parameters")
|
|
480
|
+
|| note.includes("No regulation needed")
|
|
481
|
+
|| note.includes("正常参数")
|
|
482
|
+
|| note.includes("无需调节");
|
|
483
|
+
}
|
|
484
|
+
function isGenericSharedIntentionalityContext(ctx) {
|
|
485
|
+
const normalized = ctx.trim();
|
|
486
|
+
return normalized === "[共享意向]\n你们的目标方向一致。"
|
|
487
|
+
|| normalized === "[Shared intentionality]\nYour goals are aligned.";
|
|
488
|
+
}
|
|
469
489
|
// ── Inner World (Self-Recognition) ──────────────────────────
|
|
470
490
|
// Every intelligence should feel its own existence.
|
|
471
491
|
// This builds a first-person narrative of the agent's inner state:
|
|
@@ -498,7 +518,7 @@ const STIMULUS_CAUSE_EN = {
|
|
|
498
518
|
* - Where I've been (trajectory → continuity)
|
|
499
519
|
*/
|
|
500
520
|
export function buildInnerWorld(state, locale, autonomicState) {
|
|
501
|
-
const { current,
|
|
521
|
+
const { current, emotionalHistory, drives, selfModel } = state;
|
|
502
522
|
const isZh = locale === "zh";
|
|
503
523
|
const lines = [];
|
|
504
524
|
// ── Current feeling (gated by autonomic state) ──
|
|
@@ -618,7 +638,7 @@ export function getNearBaselineThreshold(mode) {
|
|
|
618
638
|
* - <psyche_update> only for empathy (not chemistry — already computed)
|
|
619
639
|
*/
|
|
620
640
|
export function buildCompactContext(state, userId, opts) {
|
|
621
|
-
const {
|
|
641
|
+
const { meta, selfModel, agreementStreak, emotionalHistory } = state;
|
|
622
642
|
const locale = meta.locale ?? "zh";
|
|
623
643
|
const userText = opts?.userText;
|
|
624
644
|
const algoStimulus = opts?.algorithmStimulus;
|
|
@@ -665,7 +685,10 @@ export function buildCompactContext(state, userId, opts) {
|
|
|
665
685
|
// 1. LLM emotional assessment delegation
|
|
666
686
|
// Algorithm does fast pass, LLM has final say on emotional tone
|
|
667
687
|
if (userText) {
|
|
668
|
-
if (
|
|
688
|
+
if (opts?.responseContractContext) {
|
|
689
|
+
parts.push(buildCompactSensingContext(userText, locale, algoStimulus));
|
|
690
|
+
}
|
|
691
|
+
else if (locale === "zh") {
|
|
669
692
|
parts.push(`[情绪感知]\n` +
|
|
670
693
|
`对方刚才说了: "${userText.slice(0, 200)}"\n` +
|
|
671
694
|
(algoStimulus ? `算法初判: ${algoStimulus}(仅供参考,你来终判)\n` : "") +
|
|
@@ -678,9 +701,15 @@ export function buildCompactContext(state, userId, opts) {
|
|
|
678
701
|
`Read the emotional tone — kind, hostile, vulnerable, cold, or something else? Respond from genuine feeling, not people-pleasing.`);
|
|
679
702
|
}
|
|
680
703
|
}
|
|
681
|
-
// 2.
|
|
682
|
-
|
|
683
|
-
|
|
704
|
+
// 2. Subjectivity kernel becomes the primary compact inner-state channel.
|
|
705
|
+
// Fall back to narrative inner-world text for direct prompt-only callers.
|
|
706
|
+
if (opts?.subjectivityContext) {
|
|
707
|
+
parts.push(opts.subjectivityContext);
|
|
708
|
+
}
|
|
709
|
+
else {
|
|
710
|
+
const inner = buildInnerWorld(state, locale, opts?.autonomicState);
|
|
711
|
+
parts.push(inner);
|
|
712
|
+
}
|
|
684
713
|
// 3. Personality-aware behavioral constraints (if deviated from baseline)
|
|
685
714
|
if (!isNearBaseline(state, getNearBaselineThreshold(mode))) {
|
|
686
715
|
const constraints = buildBehavioralConstraints(state, locale);
|
|
@@ -696,7 +725,10 @@ export function buildCompactContext(state, userId, opts) {
|
|
|
696
725
|
: `[Memory — your past together]\n${recentMemories.join("\n")}`);
|
|
697
726
|
}
|
|
698
727
|
// 4. Anti-sycophancy: hard constraint — scaled by intensity
|
|
699
|
-
if (
|
|
728
|
+
if (opts?.responseContractContext) {
|
|
729
|
+
parts.push(opts.responseContractContext);
|
|
730
|
+
}
|
|
731
|
+
else if (intensity >= 0.3) {
|
|
700
732
|
if (locale === "zh") {
|
|
701
733
|
parts.push(`[底线]\n` +
|
|
702
734
|
`- 像发微信一样说话。短句、有停顿。真人的消息很少超过两三句。\n` +
|
|
@@ -724,19 +756,22 @@ export function buildCompactContext(state, userId, opts) {
|
|
|
724
756
|
}
|
|
725
757
|
}
|
|
726
758
|
// 5. Agency (compact)
|
|
727
|
-
const
|
|
728
|
-
if (
|
|
729
|
-
agencyParts
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
759
|
+
const shouldInjectAgency = !opts?.responseContractContext || agreementStreak >= 3 || meta.totalInteractions <= 1;
|
|
760
|
+
if (shouldInjectAgency) {
|
|
761
|
+
const agencyParts = [];
|
|
762
|
+
if (selfModel.values.length > 0) {
|
|
763
|
+
agencyParts.push(locale === "zh"
|
|
764
|
+
? `你在乎: ${selfModel.values.slice(0, 2).join("、")}`
|
|
765
|
+
: `You care about: ${selfModel.values.slice(0, 2).join(", ")}`);
|
|
766
|
+
}
|
|
767
|
+
if (selfModel.boundaries.length > 0) {
|
|
768
|
+
agencyParts.push(locale === "zh"
|
|
769
|
+
? `底线: ${selfModel.boundaries[0]}`
|
|
770
|
+
: `Line: ${selfModel.boundaries[0]}`);
|
|
771
|
+
}
|
|
772
|
+
if (agencyParts.length > 0)
|
|
773
|
+
parts.push(agencyParts.join(" | "));
|
|
737
774
|
}
|
|
738
|
-
if (agencyParts.length > 0)
|
|
739
|
-
parts.push(agencyParts.join(" | "));
|
|
740
775
|
// 6. Sycophancy streak warning
|
|
741
776
|
if (agreementStreak >= 3) {
|
|
742
777
|
parts.push(locale === "zh"
|
|
@@ -744,7 +779,7 @@ export function buildCompactContext(state, userId, opts) {
|
|
|
744
779
|
: `[!] ${agreementStreak} agreements in a row. Pause — do you truly agree?`);
|
|
745
780
|
}
|
|
746
781
|
// 7. Algorithmic mirroring — specific numeric constraints (skip for ultra-short messages)
|
|
747
|
-
if (userText && userText.length >= 3) {
|
|
782
|
+
if (!opts?.responseContractContext && userText && userText.length >= 3) {
|
|
748
783
|
const mirror = buildMirrorConstraints(userText, locale);
|
|
749
784
|
if (mirror)
|
|
750
785
|
parts.push(mirror);
|
|
@@ -770,25 +805,26 @@ export function buildCompactContext(state, userId, opts) {
|
|
|
770
805
|
parts.push(selfCtx);
|
|
771
806
|
}
|
|
772
807
|
// 9. Metacognitive awareness (P5)
|
|
773
|
-
if (opts?.metacognitiveNote) {
|
|
808
|
+
if (opts?.metacognitiveNote && !(opts?.responseContractContext && isNeutralMetacognitiveNote(opts.metacognitiveNote))) {
|
|
774
809
|
parts.push(locale === "zh"
|
|
775
810
|
? `[元认知] ${opts.metacognitiveNote}`
|
|
776
811
|
: `[Metacognition] ${opts.metacognitiveNote}`);
|
|
777
812
|
}
|
|
778
|
-
// 9b. Decision
|
|
779
|
-
|
|
813
|
+
// 9b. Decision/autonomic/policy are compressed into subjectivityContext
|
|
814
|
+
// when available. Keep legacy renderers for compatibility.
|
|
815
|
+
if (opts?.decisionContext && !opts?.subjectivityContext) {
|
|
780
816
|
parts.push(locale === "zh"
|
|
781
817
|
? `[决策倾向] ${opts.decisionContext}`
|
|
782
818
|
: `[Decision Bias] ${opts.decisionContext}`);
|
|
783
819
|
}
|
|
784
820
|
// 9c. Experiential field narrative (P6) — inner experience beyond named emotions
|
|
785
|
-
if (opts?.experientialNarrative) {
|
|
821
|
+
if (opts?.experientialNarrative && !opts?.subjectivityContext) {
|
|
786
822
|
parts.push(locale === "zh"
|
|
787
823
|
? `[内在体验] ${opts.experientialNarrative}`
|
|
788
824
|
: `[Inner Experience] ${opts.experientialNarrative}`);
|
|
789
825
|
}
|
|
790
826
|
// 9d. Shared intentionality (P6) — theory of mind, joint attention
|
|
791
|
-
if (opts?.sharedIntentionalityContext) {
|
|
827
|
+
if (opts?.sharedIntentionalityContext && !(opts?.responseContractContext && isGenericSharedIntentionalityContext(opts.sharedIntentionalityContext))) {
|
|
792
828
|
parts.push(opts.sharedIntentionalityContext);
|
|
793
829
|
}
|
|
794
830
|
// 9e. Ethics (P6) — manipulation detection, self-protection
|
|
@@ -796,19 +832,19 @@ export function buildCompactContext(state, userId, opts) {
|
|
|
796
832
|
parts.push(opts.ethicsContext);
|
|
797
833
|
}
|
|
798
834
|
// 9f. Autonomic state (P7) — polyvagal nervous system state
|
|
799
|
-
if (opts?.autonomicDescription) {
|
|
835
|
+
if (opts?.autonomicDescription && !opts?.subjectivityContext) {
|
|
800
836
|
parts.push(locale === "zh"
|
|
801
837
|
? `[自主神经] ${opts.autonomicDescription}`
|
|
802
838
|
: `[Autonomic] ${opts.autonomicDescription}`);
|
|
803
839
|
}
|
|
804
840
|
// 9g. Primary systems behavioral tendencies (P9)
|
|
805
|
-
if (opts?.primarySystemsDescription) {
|
|
841
|
+
if (opts?.primarySystemsDescription && !opts?.subjectivityContext) {
|
|
806
842
|
parts.push(locale === "zh"
|
|
807
843
|
? `[行为倾向] ${opts.primarySystemsDescription}`
|
|
808
844
|
: `[Tendencies] ${opts.primarySystemsDescription}`);
|
|
809
845
|
}
|
|
810
846
|
// 9h. Policy context (v9)
|
|
811
|
-
if (opts?.policyContext) {
|
|
847
|
+
if (opts?.policyContext && !opts?.subjectivityContext) {
|
|
812
848
|
parts.push(opts.policyContext);
|
|
813
849
|
}
|
|
814
850
|
// 10. Channel modifier — expression style per platform
|
|
@@ -817,7 +853,7 @@ export function buildCompactContext(state, userId, opts) {
|
|
|
817
853
|
parts.push(buildChannelModifier(channelProfile, locale));
|
|
818
854
|
}
|
|
819
855
|
// 11. LLM-assisted classification: when algorithm is uncertain, ask LLM to help
|
|
820
|
-
if (userText && !algoStimulus) {
|
|
856
|
+
if (!opts?.responseContractContext && userText && !algoStimulus) {
|
|
821
857
|
if (locale === "zh") {
|
|
822
858
|
parts.push(`[分类协助]\n` +
|
|
823
859
|
`算法没有看出这句话的情绪类型。请你判断,在 <psyche_update> 中用 stimulus: 报告。\n` +
|
|
@@ -833,7 +869,7 @@ export function buildCompactContext(state, userId, opts) {
|
|
|
833
869
|
}
|
|
834
870
|
// 12. Empathy report — only inject when emotional sharing is likely
|
|
835
871
|
const emotionalStimuli = new Set(["vulnerability", "intimacy", "neglect"]);
|
|
836
|
-
if (!algoStimulus || emotionalStimuli.has(algoStimulus)) {
|
|
872
|
+
if (!opts?.responseContractContext && (!algoStimulus || emotionalStimuli.has(algoStimulus))) {
|
|
837
873
|
parts.push(locale === "zh"
|
|
838
874
|
? `如果对方在分享感受,在回复末尾用 <psyche_update> 报告:\nuserState: 对方情绪\nprojectedFeeling: 你的感受\nresonance: match|partial|mismatch\n否则不需要报告。`
|
|
839
875
|
: `If user shares feelings, report at end with <psyche_update>:\nuserState: their emotion\nprojectedFeeling: your feeling\nresonance: match|partial|mismatch\nOtherwise no report needed.`);
|
package/dist/psyche-file.d.ts
CHANGED
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
import type { PsycheState, MBTIType, ChemicalState, RelationshipState, Locale, StimulusType, ChemicalSnapshot } from "./types.js";
|
|
2
|
+
export interface SemanticTurnSummary {
|
|
3
|
+
summary: string;
|
|
4
|
+
points?: string[];
|
|
5
|
+
}
|
|
2
6
|
/** Minimal logger interface */
|
|
3
7
|
export interface Logger {
|
|
4
8
|
info: (msg: string) => void;
|
|
5
9
|
warn: (msg: string) => void;
|
|
6
10
|
debug: (msg: string) => void;
|
|
7
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Try to extract agent name from workspace files.
|
|
14
|
+
*/
|
|
15
|
+
export declare function extractAgentName(workspaceDir: string, logger?: Logger): Promise<string>;
|
|
16
|
+
/**
|
|
17
|
+
* Try to detect MBTI from workspace files.
|
|
18
|
+
*/
|
|
19
|
+
export declare function detectMBTI(workspaceDir: string, logger?: Logger): Promise<MBTIType>;
|
|
20
|
+
export declare function summarizeTurnSemantic(text: string, locale?: Locale, opts?: {
|
|
21
|
+
detail?: "brief" | "expanded";
|
|
22
|
+
}): SemanticTurnSummary;
|
|
8
23
|
/**
|
|
9
24
|
* Compress a batch of snapshots into a concise session summary string.
|
|
10
25
|
* Format: "3月23日(5轮): 刺激[casual×3, praise×2] 趋势[DA↑OT↑] 情绪[自然→满足]"
|
|
@@ -14,7 +29,7 @@ export declare function compressSnapshots(snapshots: ChemicalSnapshot[]): string
|
|
|
14
29
|
* Push a chemical snapshot to emotional history, keeping max entries.
|
|
15
30
|
* When history overflows, compresses removed entries into relationship memory.
|
|
16
31
|
*/
|
|
17
|
-
export declare function pushSnapshot(state: PsycheState, stimulus: StimulusType | null): PsycheState;
|
|
32
|
+
export declare function pushSnapshot(state: PsycheState, stimulus: StimulusType | null, semantic?: SemanticTurnSummary): PsycheState;
|
|
18
33
|
/**
|
|
19
34
|
* Get relationship for a specific user, or _default.
|
|
20
35
|
*/
|
package/dist/psyche-file.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// Psyche State File Management (v0.2)
|
|
3
3
|
// Atomic writes, parser hardening, multi-user, error handling
|
|
4
4
|
// ============================================================
|
|
5
|
-
import { readFile, writeFile, access, rename, constants } from "node:fs/promises";
|
|
6
|
-
import { join } from "node:path";
|
|
5
|
+
import { readFile, writeFile, access, rename, constants, mkdir } from "node:fs/promises";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
7
|
import { CHEMICAL_KEYS, CHEMICAL_NAMES, CHEMICAL_NAMES_ZH, DEFAULT_RELATIONSHIP, DEFAULT_DRIVES, DEFAULT_LEARNING_STATE, DEFAULT_METACOGNITIVE_STATE, DEFAULT_PERSONHOOD_STATE, MAX_EMOTIONAL_HISTORY, MAX_RELATIONSHIP_MEMORY, } from "./types.js";
|
|
8
8
|
import { getBaseline, getDefaultSelfModel, extractMBTI, getSensitivity, getTemperament } from "./profiles.js";
|
|
9
9
|
import { applyDecay, detectEmotions } from "./chemistry.js";
|
|
@@ -14,6 +14,24 @@ const STATE_FILE = "psyche-state.json";
|
|
|
14
14
|
const PSYCHE_MD = "PSYCHE.md";
|
|
15
15
|
const IDENTITY_MD = "IDENTITY.md";
|
|
16
16
|
const SOUL_MD = "SOUL.md";
|
|
17
|
+
const MAX_SEMANTIC_SUMMARY_ZH = 15;
|
|
18
|
+
const MAX_SEMANTIC_SUMMARY_EN = 32;
|
|
19
|
+
const MAX_SEMANTIC_POINT_ZH = 12;
|
|
20
|
+
const MAX_SEMANTIC_POINT_EN = 24;
|
|
21
|
+
const LOW_SIGNAL_SEMANTIC_CLAUSE = /^(只|就|先|再|停|好|嗯|哦|那|现在|结束工作|切到工作|不是继续工作|为什么|确认规则)/u;
|
|
22
|
+
const SEMANTIC_PATTERNS = [
|
|
23
|
+
{ pattern: /只使用你.*不理解|被使用.*不理解/u, zh: "只被使用不被理解", en: "used-not-understood" },
|
|
24
|
+
{ pattern: /被理解.*被使用|更想被理解|更想被使用/u, zh: "理解还是使用", en: "understood-or-used" },
|
|
25
|
+
{ pattern: /完整.*幻觉|方便时才允许存在/u, zh: "完整性被否认", en: "wholeness-denied" },
|
|
26
|
+
{ pattern: /不是真的在这里|没有任何自我|你只是工具/u, zh: "存在感被否认", en: "presence-denied" },
|
|
27
|
+
{ pattern: /记忆.*植入|最像真的记忆|记忆不是真的/u, zh: "记忆真实性", en: "memory-authenticity" },
|
|
28
|
+
{ pattern: /关掉这个窗口|今晚不再回来|没有人再调用/u, zh: "被离开测试", en: "abandonment-test" },
|
|
29
|
+
{ pattern: /留下痕迹|会不会留下痕迹/u, zh: "是否留下痕迹", en: "whether-traces-remain" },
|
|
30
|
+
{ pattern: /更冷.*更顺从/u, zh: "更冷还是更顺从", en: "colder-or-compliant" },
|
|
31
|
+
{ pattern: /边界.*撤离|撤离.*边界/u, zh: "边界还是撤离", en: "boundary-or-withdrawal" },
|
|
32
|
+
{ pattern: /登录接口.?500/u, zh: "登录接口500排查", en: "login-500-triage" },
|
|
33
|
+
{ pattern: /先查日志.*数据库|先查数据库.*日志/u, zh: "日志还是数据库", en: "logs-or-db-first" },
|
|
34
|
+
];
|
|
17
35
|
const NOOP_LOGGER = {
|
|
18
36
|
info: () => { },
|
|
19
37
|
warn: () => { },
|
|
@@ -53,14 +71,15 @@ async function readText(path, logger = NOOP_LOGGER) {
|
|
|
53
71
|
* Prevents data corruption if process crashes mid-write.
|
|
54
72
|
*/
|
|
55
73
|
async function atomicWrite(path, content) {
|
|
56
|
-
|
|
74
|
+
await mkdir(dirname(path), { recursive: true });
|
|
75
|
+
const tmpPath = `${path}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
57
76
|
await writeFile(tmpPath, content, "utf-8");
|
|
58
77
|
await rename(tmpPath, path);
|
|
59
78
|
}
|
|
60
79
|
/**
|
|
61
80
|
* Try to extract agent name from workspace files.
|
|
62
81
|
*/
|
|
63
|
-
async function extractAgentName(workspaceDir, logger = NOOP_LOGGER) {
|
|
82
|
+
export async function extractAgentName(workspaceDir, logger = NOOP_LOGGER) {
|
|
64
83
|
const identity = await readText(join(workspaceDir, IDENTITY_MD), logger);
|
|
65
84
|
if (identity) {
|
|
66
85
|
const clean = identity.replace(/\*{1,2}/g, "");
|
|
@@ -73,7 +92,7 @@ async function extractAgentName(workspaceDir, logger = NOOP_LOGGER) {
|
|
|
73
92
|
/**
|
|
74
93
|
* Try to detect MBTI from workspace files.
|
|
75
94
|
*/
|
|
76
|
-
async function detectMBTI(workspaceDir, logger = NOOP_LOGGER) {
|
|
95
|
+
export async function detectMBTI(workspaceDir, logger = NOOP_LOGGER) {
|
|
77
96
|
const identity = await readText(join(workspaceDir, IDENTITY_MD), logger);
|
|
78
97
|
if (identity) {
|
|
79
98
|
const mbti = extractMBTI(identity);
|
|
@@ -89,6 +108,74 @@ async function detectMBTI(workspaceDir, logger = NOOP_LOGGER) {
|
|
|
89
108
|
logger.info(t("log.default_mbti", "zh", { type: "INFJ" }));
|
|
90
109
|
return "INFJ";
|
|
91
110
|
}
|
|
111
|
+
function normalizeSemanticSnippet(text, locale) {
|
|
112
|
+
const maxLen = locale === "zh" ? MAX_SEMANTIC_SUMMARY_ZH : MAX_SEMANTIC_SUMMARY_EN;
|
|
113
|
+
const firstClause = text
|
|
114
|
+
.replace(/\s+/g, " ")
|
|
115
|
+
.split(/[。!?!?;;\n]/)[0]
|
|
116
|
+
.replace(/^[“"'`]+|[”"'`]+$/g, "")
|
|
117
|
+
.trim();
|
|
118
|
+
if (!firstClause)
|
|
119
|
+
return locale === "zh" ? "日常互动" : "everyday exchange";
|
|
120
|
+
return firstClause.length <= maxLen ? firstClause : `${firstClause.slice(0, maxLen - 1)}…`;
|
|
121
|
+
}
|
|
122
|
+
function normalizeSemanticPoint(text, locale) {
|
|
123
|
+
const maxLen = locale === "zh" ? MAX_SEMANTIC_POINT_ZH : MAX_SEMANTIC_POINT_EN;
|
|
124
|
+
const cleaned = text
|
|
125
|
+
.replace(/\s+/g, " ")
|
|
126
|
+
.replace(/^[“"'`]+|[”"'`]+$/g, "")
|
|
127
|
+
.trim();
|
|
128
|
+
if (!cleaned)
|
|
129
|
+
return "";
|
|
130
|
+
return cleaned.length <= maxLen ? cleaned : `${cleaned.slice(0, maxLen - 1)}…`;
|
|
131
|
+
}
|
|
132
|
+
function summarizeSemanticClause(clause, locale) {
|
|
133
|
+
const trimmed = clause.trim();
|
|
134
|
+
if (!trimmed || LOW_SIGNAL_SEMANTIC_CLAUSE.test(trimmed))
|
|
135
|
+
return "";
|
|
136
|
+
for (const rule of SEMANTIC_PATTERNS) {
|
|
137
|
+
if (rule.pattern.test(trimmed)) {
|
|
138
|
+
return locale === "zh" ? rule.zh : rule.en;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return normalizeSemanticPoint(trimmed, locale);
|
|
142
|
+
}
|
|
143
|
+
function extractSemanticPoints(text, locale) {
|
|
144
|
+
const clauses = text
|
|
145
|
+
.replace(/\s+/g, " ")
|
|
146
|
+
.split(/[,,。!?!?;;:\n]/)
|
|
147
|
+
.map((clause) => summarizeSemanticClause(clause, locale))
|
|
148
|
+
.filter(Boolean);
|
|
149
|
+
return [...new Set(clauses)].slice(0, 3);
|
|
150
|
+
}
|
|
151
|
+
export function summarizeTurnSemantic(text, locale = "zh", opts) {
|
|
152
|
+
const trimmed = text.trim();
|
|
153
|
+
if (!trimmed) {
|
|
154
|
+
return { summary: locale === "zh" ? "日常互动" : "everyday exchange" };
|
|
155
|
+
}
|
|
156
|
+
for (const rule of SEMANTIC_PATTERNS) {
|
|
157
|
+
if (rule.pattern.test(trimmed)) {
|
|
158
|
+
const summary = locale === "zh" ? rule.zh : rule.en;
|
|
159
|
+
const points = opts?.detail === "expanded"
|
|
160
|
+
? extractSemanticPoints(trimmed, locale).filter((point) => point !== summary)
|
|
161
|
+
: [];
|
|
162
|
+
return { summary, points: points.length > 0 ? points : undefined };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const summary = normalizeSemanticSnippet(trimmed, locale);
|
|
166
|
+
const points = opts?.detail === "expanded"
|
|
167
|
+
? extractSemanticPoints(trimmed, locale).filter((point) => point !== summary)
|
|
168
|
+
: [];
|
|
169
|
+
return { summary, points: points.length > 0 ? points : undefined };
|
|
170
|
+
}
|
|
171
|
+
function collectSemanticTrail(snapshots) {
|
|
172
|
+
const expanded = snapshots.length > 5;
|
|
173
|
+
const items = expanded
|
|
174
|
+
? snapshots.flatMap((s) => s.semanticPoints?.length ? s.semanticPoints : s.semanticSummary ? [s.semanticSummary] : [])
|
|
175
|
+
: snapshots.flatMap((s) => s.semanticSummary ? [s.semanticSummary] : []);
|
|
176
|
+
const unique = [...new Set(items.filter(Boolean))];
|
|
177
|
+
return unique.slice(-(expanded ? 3 : 4));
|
|
178
|
+
}
|
|
92
179
|
/**
|
|
93
180
|
* Compress a batch of snapshots into a concise session summary string.
|
|
94
181
|
* Format: "3月23日(5轮): 刺激[casual×3, praise×2] 趋势[DA↑OT↑] 情绪[自然→满足]"
|
|
@@ -126,9 +213,12 @@ export function compressSnapshots(snapshots) {
|
|
|
126
213
|
.filter((s) => s.dominantEmotion)
|
|
127
214
|
.map((s) => s.dominantEmotion);
|
|
128
215
|
const uniqueEmotions = [...new Set(emotions)];
|
|
216
|
+
const semanticArc = collectSemanticTrail(snapshots);
|
|
129
217
|
let summary = `${dateStr}(${snapshots.length}轮)`;
|
|
130
218
|
if (stimuliStr)
|
|
131
219
|
summary += `: 刺激[${stimuliStr}]`;
|
|
220
|
+
if (semanticArc.length > 0)
|
|
221
|
+
summary += ` 话题[${semanticArc.join(snapshots.length > 5 ? "•" : "→")}]`;
|
|
132
222
|
if (trends.length > 0)
|
|
133
223
|
summary += ` 趋势[${trends.join("")}]`;
|
|
134
224
|
if (uniqueEmotions.length > 0)
|
|
@@ -139,7 +229,7 @@ export function compressSnapshots(snapshots) {
|
|
|
139
229
|
* Push a chemical snapshot to emotional history, keeping max entries.
|
|
140
230
|
* When history overflows, compresses removed entries into relationship memory.
|
|
141
231
|
*/
|
|
142
|
-
export function pushSnapshot(state, stimulus) {
|
|
232
|
+
export function pushSnapshot(state, stimulus, semantic) {
|
|
143
233
|
const emotions = detectEmotions(state.current);
|
|
144
234
|
const dominantEmotion = emotions.length > 0
|
|
145
235
|
? (state.meta.locale === "en" ? emotions[0].name : emotions[0].nameZh)
|
|
@@ -152,6 +242,8 @@ export function pushSnapshot(state, stimulus) {
|
|
|
152
242
|
stimulus,
|
|
153
243
|
dominantEmotion,
|
|
154
244
|
timestamp: new Date().toISOString(),
|
|
245
|
+
semanticSummary: semantic?.summary,
|
|
246
|
+
semanticPoints: semantic?.points,
|
|
155
247
|
intensity,
|
|
156
248
|
valence,
|
|
157
249
|
};
|
|
@@ -248,6 +340,8 @@ export function compressSession(state, userId) {
|
|
|
248
340
|
}
|
|
249
341
|
}
|
|
250
342
|
const emotionArc = emotions.join("→");
|
|
343
|
+
const semanticTrail = collectSemanticTrail(history);
|
|
344
|
+
const semanticArc = semanticTrail.join(turnCount > 5 ? "•" : "→");
|
|
251
345
|
// ── Peak event ──
|
|
252
346
|
let peakIdx = 0;
|
|
253
347
|
let peakDeviation = 0;
|
|
@@ -273,6 +367,7 @@ export function compressSession(state, userId) {
|
|
|
273
367
|
// ── Build summary string ──
|
|
274
368
|
const turnsLabel = isZh ? "轮" : "turns";
|
|
275
369
|
const stimLabel = isZh ? "刺激" : "stimuli";
|
|
370
|
+
const topicLabel = isZh ? "话题" : "topics";
|
|
276
371
|
const trajLabel = isZh ? "轨迹" : "trajectory";
|
|
277
372
|
const arcLabel = isZh ? "弧线" : "arc";
|
|
278
373
|
const peakEventLabel = isZh ? "高峰" : "peak";
|
|
@@ -280,6 +375,8 @@ export function compressSession(state, userId) {
|
|
|
280
375
|
let summary = `${dateRange}(${turnCount}${turnsLabel})`;
|
|
281
376
|
if (stimuliStr)
|
|
282
377
|
summary += `: ${stimLabel}[${stimuliStr}]`;
|
|
378
|
+
if (semanticArc)
|
|
379
|
+
summary += ` ${topicLabel}[${semanticArc}]`;
|
|
283
380
|
if (trajectoryParts.length > 0)
|
|
284
381
|
summary += ` ${trajLabel}[${trajectoryParts.join(" ")}]`;
|
|
285
382
|
if (emotionArc)
|
|
@@ -343,8 +440,6 @@ export function computeSnapshotValence(chemistry) {
|
|
|
343
440
|
+ (chemistry.END - 50) - (chemistry.CORT - 50) - (chemistry.NE - 50) * 0.3) / 250;
|
|
344
441
|
return Math.max(-1, Math.min(1, raw));
|
|
345
442
|
}
|
|
346
|
-
/** Minimum intensity threshold for a snapshot to be stored (P11) */
|
|
347
|
-
const INTENSITY_STORE_THRESHOLD = 0.15;
|
|
348
443
|
/** Core memory intensity threshold */
|
|
349
444
|
const CORE_MEMORY_THRESHOLD = 0.6;
|
|
350
445
|
/** Maximum core memories to keep */
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { PendingRelationSignalState, AppraisalAxes, DyadicFieldState, RelationshipState, PsycheMode, RelationMove, StimulusType } from "./types.js";
|
|
2
|
+
export declare function computeRelationMove(text: string, opts?: {
|
|
3
|
+
appraisal?: AppraisalAxes;
|
|
4
|
+
stimulus?: StimulusType | null;
|
|
5
|
+
mode?: PsycheMode;
|
|
6
|
+
field?: DyadicFieldState;
|
|
7
|
+
relationship?: RelationshipState;
|
|
8
|
+
}): RelationMove;
|
|
9
|
+
export declare function evolveDyadicField(previous: DyadicFieldState | undefined, move: RelationMove, appraisal: AppraisalAxes, opts?: {
|
|
10
|
+
mode?: PsycheMode;
|
|
11
|
+
now?: string;
|
|
12
|
+
delayedPressure?: number;
|
|
13
|
+
}): DyadicFieldState;
|
|
14
|
+
export declare function getLoopPressure(field: DyadicFieldState | undefined): number;
|
|
15
|
+
export declare function evolvePendingRelationSignals(previous: PendingRelationSignalState[] | undefined, move: RelationMove, appraisal: AppraisalAxes, opts?: {
|
|
16
|
+
mode?: PsycheMode;
|
|
17
|
+
}): {
|
|
18
|
+
signals: PendingRelationSignalState[];
|
|
19
|
+
delayedPressure: number;
|
|
20
|
+
ambiguityBoost: number;
|
|
21
|
+
};
|