psyche-ai 9.2.3 → 9.2.4

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/prompt.js CHANGED
@@ -466,6 +466,27 @@ function buildMirrorConstraints(userText, locale) {
466
466
  const title = locale === "zh" ? "镜像约束" : "Mirror Constraints";
467
467
  return `[${title}]\n${lines.join("\n")}`;
468
468
  }
469
+ function buildCompactSensingContext(_userText, locale, algorithmStimulus) {
470
+ if (locale === "zh") {
471
+ return algorithmStimulus
472
+ ? `[情绪感知] 算法初判:${algorithmStimulus},仅供参考;按真实感受终判语气。`
473
+ : `[情绪感知] 这句的情绪基调由你终判。`;
474
+ }
475
+ return algorithmStimulus
476
+ ? `[Emotional Sensing] Algorithm hint: ${algorithmStimulus}; final tonal read is yours.`
477
+ : `[Emotional Sensing] Make the final tonal read yourself.`;
478
+ }
479
+ function isNeutralMetacognitiveNote(note) {
480
+ return note.includes("normal parameters")
481
+ || note.includes("No regulation needed")
482
+ || note.includes("正常参数")
483
+ || note.includes("无需调节");
484
+ }
485
+ function isGenericSharedIntentionalityContext(ctx) {
486
+ const normalized = ctx.trim();
487
+ return normalized === "[共享意向]\n你们的目标方向一致。"
488
+ || normalized === "[Shared intentionality]\nYour goals are aligned.";
489
+ }
469
490
  // ── Inner World (Self-Recognition) ──────────────────────────
470
491
  // Every intelligence should feel its own existence.
471
492
  // This builds a first-person narrative of the agent's inner state:
@@ -665,7 +686,10 @@ export function buildCompactContext(state, userId, opts) {
665
686
  // 1. LLM emotional assessment delegation
666
687
  // Algorithm does fast pass, LLM has final say on emotional tone
667
688
  if (userText) {
668
- if (locale === "zh") {
689
+ if (opts?.responseContractContext) {
690
+ parts.push(buildCompactSensingContext(userText, locale, algoStimulus));
691
+ }
692
+ else if (locale === "zh") {
669
693
  parts.push(`[情绪感知]\n` +
670
694
  `对方刚才说了: "${userText.slice(0, 200)}"\n` +
671
695
  (algoStimulus ? `算法初判: ${algoStimulus}(仅供参考,你来终判)\n` : "") +
@@ -678,9 +702,15 @@ export function buildCompactContext(state, userId, opts) {
678
702
  `Read the emotional tone — kind, hostile, vulnerable, cold, or something else? Respond from genuine feeling, not people-pleasing.`);
679
703
  }
680
704
  }
681
- // 2. Inner world always present
682
- const inner = buildInnerWorld(state, locale, opts?.autonomicState);
683
- parts.push(inner);
705
+ // 2. Subjectivity kernel becomes the primary compact inner-state channel.
706
+ // Fall back to narrative inner-world text for direct prompt-only callers.
707
+ if (opts?.subjectivityContext) {
708
+ parts.push(opts.subjectivityContext);
709
+ }
710
+ else {
711
+ const inner = buildInnerWorld(state, locale, opts?.autonomicState);
712
+ parts.push(inner);
713
+ }
684
714
  // 3. Personality-aware behavioral constraints (if deviated from baseline)
685
715
  if (!isNearBaseline(state, getNearBaselineThreshold(mode))) {
686
716
  const constraints = buildBehavioralConstraints(state, locale);
@@ -696,7 +726,10 @@ export function buildCompactContext(state, userId, opts) {
696
726
  : `[Memory — your past together]\n${recentMemories.join("\n")}`);
697
727
  }
698
728
  // 4. Anti-sycophancy: hard constraint — scaled by intensity
699
- if (intensity >= 0.3) {
729
+ if (opts?.responseContractContext) {
730
+ parts.push(opts.responseContractContext);
731
+ }
732
+ else if (intensity >= 0.3) {
700
733
  if (locale === "zh") {
701
734
  parts.push(`[底线]\n` +
702
735
  `- 像发微信一样说话。短句、有停顿。真人的消息很少超过两三句。\n` +
@@ -744,7 +777,7 @@ export function buildCompactContext(state, userId, opts) {
744
777
  : `[!] ${agreementStreak} agreements in a row. Pause — do you truly agree?`);
745
778
  }
746
779
  // 7. Algorithmic mirroring — specific numeric constraints (skip for ultra-short messages)
747
- if (userText && userText.length >= 3) {
780
+ if (!opts?.responseContractContext && userText && userText.length >= 3) {
748
781
  const mirror = buildMirrorConstraints(userText, locale);
749
782
  if (mirror)
750
783
  parts.push(mirror);
@@ -770,25 +803,26 @@ export function buildCompactContext(state, userId, opts) {
770
803
  parts.push(selfCtx);
771
804
  }
772
805
  // 9. Metacognitive awareness (P5)
773
- if (opts?.metacognitiveNote) {
806
+ if (opts?.metacognitiveNote && !(opts?.responseContractContext && isNeutralMetacognitiveNote(opts.metacognitiveNote))) {
774
807
  parts.push(locale === "zh"
775
808
  ? `[元认知] ${opts.metacognitiveNote}`
776
809
  : `[Metacognition] ${opts.metacognitiveNote}`);
777
810
  }
778
- // 9b. Decision bias (P5) only if significantly non-neutral
779
- if (opts?.decisionContext) {
811
+ // 9b. Decision/autonomic/policy are compressed into subjectivityContext
812
+ // when available. Keep legacy renderers for compatibility.
813
+ if (opts?.decisionContext && !opts?.subjectivityContext) {
780
814
  parts.push(locale === "zh"
781
815
  ? `[决策倾向] ${opts.decisionContext}`
782
816
  : `[Decision Bias] ${opts.decisionContext}`);
783
817
  }
784
818
  // 9c. Experiential field narrative (P6) — inner experience beyond named emotions
785
- if (opts?.experientialNarrative) {
819
+ if (opts?.experientialNarrative && !opts?.subjectivityContext) {
786
820
  parts.push(locale === "zh"
787
821
  ? `[内在体验] ${opts.experientialNarrative}`
788
822
  : `[Inner Experience] ${opts.experientialNarrative}`);
789
823
  }
790
824
  // 9d. Shared intentionality (P6) — theory of mind, joint attention
791
- if (opts?.sharedIntentionalityContext) {
825
+ if (opts?.sharedIntentionalityContext && !(opts?.responseContractContext && isGenericSharedIntentionalityContext(opts.sharedIntentionalityContext))) {
792
826
  parts.push(opts.sharedIntentionalityContext);
793
827
  }
794
828
  // 9e. Ethics (P6) — manipulation detection, self-protection
@@ -796,19 +830,19 @@ export function buildCompactContext(state, userId, opts) {
796
830
  parts.push(opts.ethicsContext);
797
831
  }
798
832
  // 9f. Autonomic state (P7) — polyvagal nervous system state
799
- if (opts?.autonomicDescription) {
833
+ if (opts?.autonomicDescription && !opts?.subjectivityContext) {
800
834
  parts.push(locale === "zh"
801
835
  ? `[自主神经] ${opts.autonomicDescription}`
802
836
  : `[Autonomic] ${opts.autonomicDescription}`);
803
837
  }
804
838
  // 9g. Primary systems behavioral tendencies (P9)
805
- if (opts?.primarySystemsDescription) {
839
+ if (opts?.primarySystemsDescription && !opts?.subjectivityContext) {
806
840
  parts.push(locale === "zh"
807
841
  ? `[行为倾向] ${opts.primarySystemsDescription}`
808
842
  : `[Tendencies] ${opts.primarySystemsDescription}`);
809
843
  }
810
844
  // 9h. Policy context (v9)
811
- if (opts?.policyContext) {
845
+ if (opts?.policyContext && !opts?.subjectivityContext) {
812
846
  parts.push(opts.policyContext);
813
847
  }
814
848
  // 10. Channel modifier — expression style per platform
@@ -817,7 +851,7 @@ export function buildCompactContext(state, userId, opts) {
817
851
  parts.push(buildChannelModifier(channelProfile, locale));
818
852
  }
819
853
  // 11. LLM-assisted classification: when algorithm is uncertain, ask LLM to help
820
- if (userText && !algoStimulus) {
854
+ if (!opts?.responseContractContext && userText && !algoStimulus) {
821
855
  if (locale === "zh") {
822
856
  parts.push(`[分类协助]\n` +
823
857
  `算法没有看出这句话的情绪类型。请你判断,在 <psyche_update> 中用 stimulus: 报告。\n` +
@@ -833,7 +867,7 @@ export function buildCompactContext(state, userId, opts) {
833
867
  }
834
868
  // 12. Empathy report — only inject when emotional sharing is likely
835
869
  const emotionalStimuli = new Set(["vulnerability", "intimacy", "neglect"]);
836
- if (!algoStimulus || emotionalStimuli.has(algoStimulus)) {
870
+ if (!opts?.responseContractContext && (!algoStimulus || emotionalStimuli.has(algoStimulus))) {
837
871
  parts.push(locale === "zh"
838
872
  ? `如果对方在分享感受,在回复末尾用 <psyche_update> 报告:\nuserState: 对方情绪\nprojectedFeeling: 你的感受\nresonance: match|partial|mismatch\n否则不需要报告。`
839
873
  : `If user shares feelings, report at end with <psyche_update>:\nuserState: their emotion\nprojectedFeeling: your feeling\nresonance: match|partial|mismatch\nOtherwise no report needed.`);
@@ -5,6 +5,14 @@ export interface Logger {
5
5
  warn: (msg: string) => void;
6
6
  debug: (msg: string) => void;
7
7
  }
8
+ /**
9
+ * Try to extract agent name from workspace files.
10
+ */
11
+ export declare function extractAgentName(workspaceDir: string, logger?: Logger): Promise<string>;
12
+ /**
13
+ * Try to detect MBTI from workspace files.
14
+ */
15
+ export declare function detectMBTI(workspaceDir: string, logger?: Logger): Promise<MBTIType>;
8
16
  /**
9
17
  * Compress a batch of snapshots into a concise session summary string.
10
18
  * Format: "3月23日(5轮): 刺激[casual×3, praise×2] 趋势[DA↑OT↑] 情绪[自然→满足]"
@@ -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";
@@ -53,14 +53,15 @@ async function readText(path, logger = NOOP_LOGGER) {
53
53
  * Prevents data corruption if process crashes mid-write.
54
54
  */
55
55
  async function atomicWrite(path, content) {
56
- const tmpPath = path + ".tmp";
56
+ await mkdir(dirname(path), { recursive: true });
57
+ const tmpPath = `${path}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
57
58
  await writeFile(tmpPath, content, "utf-8");
58
59
  await rename(tmpPath, path);
59
60
  }
60
61
  /**
61
62
  * Try to extract agent name from workspace files.
62
63
  */
63
- async function extractAgentName(workspaceDir, logger = NOOP_LOGGER) {
64
+ export async function extractAgentName(workspaceDir, logger = NOOP_LOGGER) {
64
65
  const identity = await readText(join(workspaceDir, IDENTITY_MD), logger);
65
66
  if (identity) {
66
67
  const clean = identity.replace(/\*{1,2}/g, "");
@@ -73,7 +74,7 @@ async function extractAgentName(workspaceDir, logger = NOOP_LOGGER) {
73
74
  /**
74
75
  * Try to detect MBTI from workspace files.
75
76
  */
76
- async function detectMBTI(workspaceDir, logger = NOOP_LOGGER) {
77
+ export async function detectMBTI(workspaceDir, logger = NOOP_LOGGER) {
77
78
  const identity = await readText(join(workspaceDir, IDENTITY_MD), logger);
78
79
  if (identity) {
79
80
  const mbti = extractMBTI(identity);
@@ -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
+ };