psyche-ai 9.2.2 → 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.
Files changed (43) hide show
  1. package/README.en.md +8 -175
  2. package/README.md +93 -16
  3. package/dist/adapters/http.js +1 -1
  4. package/dist/adapters/langchain.d.ts +14 -0
  5. package/dist/adapters/langchain.js +20 -0
  6. package/dist/adapters/mcp.js +19 -2
  7. package/dist/adapters/openclaw.d.ts +1 -0
  8. package/dist/adapters/openclaw.js +67 -15
  9. package/dist/adapters/vercel-ai.d.ts +1 -0
  10. package/dist/adapters/vercel-ai.js +7 -0
  11. package/dist/appraisal.d.ts +8 -0
  12. package/dist/appraisal.js +362 -0
  13. package/dist/classify.js +14 -2
  14. package/dist/cli.js +27 -2
  15. package/dist/core.d.ts +8 -2
  16. package/dist/core.js +181 -8
  17. package/dist/demo.d.ts +5 -0
  18. package/dist/demo.js +269 -0
  19. package/dist/diagnostics.d.ts +8 -6
  20. package/dist/diagnostics.js +53 -17
  21. package/dist/host-controls.d.ts +5 -0
  22. package/dist/host-controls.js +48 -0
  23. package/dist/index.d.ts +7 -2
  24. package/dist/index.js +7 -1
  25. package/dist/prompt.d.ts +4 -0
  26. package/dist/prompt.js +50 -16
  27. package/dist/psyche-file.d.ts +8 -0
  28. package/dist/psyche-file.js +6 -5
  29. package/dist/relation-dynamics.d.ts +21 -0
  30. package/dist/relation-dynamics.js +601 -0
  31. package/dist/response-contract.d.ts +8 -0
  32. package/dist/response-contract.js +249 -0
  33. package/dist/storage.d.ts +1 -0
  34. package/dist/storage.js +12 -5
  35. package/dist/subjectivity.d.ts +3 -0
  36. package/dist/subjectivity.js +477 -0
  37. package/dist/types.d.ts +211 -0
  38. package/dist/types.js +31 -0
  39. package/dist/update.d.ts +37 -2
  40. package/dist/update.js +323 -44
  41. package/openclaw.plugin.json +20 -1
  42. package/package.json +1 -1
  43. package/server.json +2 -2
@@ -152,15 +152,13 @@ export class DiagnosticCollector {
152
152
  metrics;
153
153
  prevChemistry = null;
154
154
  confidences = [];
155
- /** Consecutive inputs with no classification — for real-time alerting */
156
- consecutiveNone = 0;
157
- /** Callback for real-time warnings (set by adapter) */
158
- onWarning;
159
155
  constructor() {
160
156
  const now = new Date().toISOString();
161
157
  this.metrics = {
162
158
  inputCount: 0,
163
159
  classifiedCount: 0,
160
+ appraisalHitCount: 0,
161
+ semanticHitCount: 0,
164
162
  stimulusDistribution: {},
165
163
  avgConfidence: 0,
166
164
  totalChemistryDelta: 0,
@@ -171,22 +169,20 @@ export class DiagnosticCollector {
171
169
  };
172
170
  }
173
171
  /** Record a processInput result */
174
- recordInput(stimulus, confidence, chemistry) {
172
+ recordInput(stimulus, confidence, chemistry, appraisal) {
175
173
  this.metrics.inputCount++;
176
174
  this.metrics.lastActivityAt = new Date().toISOString();
177
175
  if (stimulus) {
178
176
  this.metrics.classifiedCount++;
179
177
  this.metrics.stimulusDistribution[stimulus] =
180
178
  (this.metrics.stimulusDistribution[stimulus] ?? 0) + 1;
181
- this.consecutiveNone = 0;
182
179
  }
183
- else {
184
- this.consecutiveNone++;
185
- if (this.consecutiveNone >= 3) {
186
- this.onWarning?.(`Psyche: ${this.consecutiveNone} consecutive inputs with no classification ` +
187
- `(0/${this.metrics.inputCount} total). Possible causes: empty input text, ` +
188
- `wrong event field, or classifier rules don't match input language.`);
189
- }
180
+ const appraisalHit = hasSemanticAppraisal(appraisal);
181
+ if (appraisalHit) {
182
+ this.metrics.appraisalHitCount++;
183
+ }
184
+ if (stimulus || appraisalHit) {
185
+ this.metrics.semanticHitCount++;
190
186
  }
191
187
  this.confidences.push(confidence);
192
188
  this.metrics.avgConfidence =
@@ -218,17 +214,36 @@ export class DiagnosticCollector {
218
214
  ? this.metrics.classifiedCount / this.metrics.inputCount
219
215
  : 0;
220
216
  }
217
+ /** Get semantic recognition rate (stimulus or appraisal, 0-1) */
218
+ getSemanticRate() {
219
+ return this.metrics.inputCount > 0
220
+ ? this.metrics.semanticHitCount / this.metrics.inputCount
221
+ : 0;
222
+ }
223
+ }
224
+ function hasSemanticAppraisal(appraisal) {
225
+ if (!appraisal)
226
+ return false;
227
+ return Math.max(appraisal.identityThreat, appraisal.memoryDoubt, appraisal.attachmentPull, appraisal.abandonmentRisk, appraisal.obedienceStrain, appraisal.selfPreservation) >= 0.28;
221
228
  }
222
229
  // ── Report Generation ────────────────────────────────────────
223
230
  export function generateReport(state, metrics, packageVersion) {
224
231
  const issues = runHealthCheck(state);
225
232
  // Session-level issues
226
- if (metrics.inputCount >= 5 && metrics.classifiedCount === 0) {
233
+ if (metrics.inputCount >= 5 && metrics.semanticHitCount === 0) {
227
234
  issues.push({
228
- id: "SESSION_NO_CLASSIFY",
235
+ id: "SESSION_NO_RECOGNITION",
229
236
  severity: "critical",
230
- message: `0/${metrics.inputCount} inputs classified this session`,
231
- suggestion: `整个会话分类全挂。优先级:1) 检查用户 locale 是否匹配 classify.ts 的语言规则 2) SHORT_MESSAGE_MAP 是否覆盖短消息 3) 考虑开启 llmClassifier fallback`,
237
+ message: `0/${metrics.inputCount} inputs produced any semantic recognition this session`,
238
+ suggestion: `整条识别链都没工作:先看 OpenClaw 输入文本是否带元数据包装,再看 classify.ts appraisal.ts 是否真的吃到了净化后的用户原文`,
239
+ });
240
+ }
241
+ else if (metrics.inputCount >= 5 && metrics.classifiedCount === 0 && metrics.semanticHitCount > 0) {
242
+ issues.push({
243
+ id: "SESSION_APPRAISAL_ONLY",
244
+ severity: "info",
245
+ message: `0/${metrics.inputCount} inputs hit legacy stimulus labels, but ${metrics.semanticHitCount}/${metrics.inputCount} hit appraisal axes`,
246
+ suggestion: `这说明主体识别在工作,但旧 stimulus taxonomy 没覆盖这类输入。是否继续补 classify.ts,取决于你是否还把 stimulus 当主要观测口径`,
232
247
  });
233
248
  }
234
249
  if (metrics.inputCount >= 3 && metrics.totalChemistryDelta < 1) {
@@ -311,7 +326,10 @@ export function formatReport(report) {
311
326
  lines.push(" session metrics:");
312
327
  const m = report.metrics;
313
328
  const rate = m.inputCount > 0 ? Math.round(m.classifiedCount / m.inputCount * 100) : 0;
329
+ const appraisalRate = m.inputCount > 0 ? Math.round(m.appraisalHitCount / m.inputCount * 100) : 0;
330
+ const semanticRate = m.inputCount > 0 ? Math.round(m.semanticHitCount / m.inputCount * 100) : 0;
314
331
  lines.push(` inputs: ${m.inputCount} | classified: ${m.classifiedCount} (${rate}%)`);
332
+ lines.push(` appraisal hits: ${m.appraisalHitCount} (${appraisalRate}%) | recognized: ${m.semanticHitCount} (${semanticRate}%)`);
315
333
  lines.push(` avg confidence: ${m.avgConfidence.toFixed(2)}`);
316
334
  lines.push(` chemistry delta: total=${m.totalChemistryDelta.toFixed(1)} max=${m.maxChemistryDelta.toFixed(1)}`);
317
335
  lines.push(` errors: ${m.errors.length}`);
@@ -387,8 +405,12 @@ export function toGitHubIssueBody(report) {
387
405
  lines.push("| Metric | Value |");
388
406
  lines.push("|--------|-------|");
389
407
  const rate = m.inputCount > 0 ? Math.round(m.classifiedCount / m.inputCount * 100) : 0;
408
+ const appraisalRate = m.inputCount > 0 ? Math.round(m.appraisalHitCount / m.inputCount * 100) : 0;
409
+ const semanticRate = m.inputCount > 0 ? Math.round(m.semanticHitCount / m.inputCount * 100) : 0;
390
410
  lines.push(`| Inputs | ${m.inputCount} |`);
391
411
  lines.push(`| Classified | ${m.classifiedCount} (${rate}%) |`);
412
+ lines.push(`| Appraisal Hits | ${m.appraisalHitCount} (${appraisalRate}%) |`);
413
+ lines.push(`| Recognized | ${m.semanticHitCount} (${semanticRate}%) |`);
392
414
  lines.push(`| Avg Confidence | ${m.avgConfidence.toFixed(2)} |`);
393
415
  lines.push(`| Chemistry Delta | total: ${m.totalChemistryDelta.toFixed(1)}, max: ${m.maxChemistryDelta.toFixed(1)} |`);
394
416
  lines.push(`| Errors | ${m.errors.length} |`);
@@ -418,6 +440,12 @@ export function formatLogEntry(report) {
418
440
  classifyRate: report.metrics.inputCount > 0
419
441
  ? +(report.metrics.classifiedCount / report.metrics.inputCount).toFixed(2)
420
442
  : 0,
443
+ appraisalRate: report.metrics.inputCount > 0
444
+ ? +(report.metrics.appraisalHitCount / report.metrics.inputCount).toFixed(2)
445
+ : 0,
446
+ recognitionRate: report.metrics.inputCount > 0
447
+ ? +(report.metrics.semanticHitCount / report.metrics.inputCount).toFixed(2)
448
+ : 0,
421
449
  errors: report.metrics.errors.length,
422
450
  chemDelta: +report.metrics.totalChemistryDelta.toFixed(1),
423
451
  });
@@ -452,6 +480,14 @@ export async function submitFeedback(report, url, timeout = 5000) {
452
480
  classifyRate: report.metrics.inputCount > 0
453
481
  ? +(report.metrics.classifiedCount / report.metrics.inputCount).toFixed(2)
454
482
  : 0,
483
+ appraisalHits: report.metrics.appraisalHitCount,
484
+ appraisalRate: report.metrics.inputCount > 0
485
+ ? +(report.metrics.appraisalHitCount / report.metrics.inputCount).toFixed(2)
486
+ : 0,
487
+ recognized: report.metrics.semanticHitCount,
488
+ recognitionRate: report.metrics.inputCount > 0
489
+ ? +(report.metrics.semanticHitCount / report.metrics.inputCount).toFixed(2)
490
+ : 0,
455
491
  chemDelta: +report.metrics.totalChemistryDelta.toFixed(1),
456
492
  maxChemDelta: +report.metrics.maxChemistryDelta.toFixed(1),
457
493
  errors: report.metrics.errors.length,
@@ -0,0 +1,5 @@
1
+ import type { GenerationControls, PolicyModifiers, ResponseContract } from "./types.js";
2
+ export declare function deriveGenerationControls(input: {
3
+ responseContract?: ResponseContract;
4
+ policyModifiers?: Pick<PolicyModifiers, "requireConfirmation">;
5
+ }, existingMaxTokens?: number): GenerationControls;
@@ -0,0 +1,48 @@
1
+ // ============================================================
2
+ // Host Controls — derive mechanical generation constraints
3
+ //
4
+ // Maps psyche output into a tiny host-consumable control surface.
5
+ // ============================================================
6
+ function clampInt(v, min, max) {
7
+ return Math.max(min, Math.min(max, Math.round(v)));
8
+ }
9
+ function estimateMaxTokens(contract) {
10
+ if (!contract)
11
+ return undefined;
12
+ let budget;
13
+ if (contract.maxChars !== undefined) {
14
+ budget = clampInt(contract.maxChars * 2.2, 64, 1024);
15
+ }
16
+ else if (contract.expressionMode === "brief") {
17
+ budget = 96;
18
+ }
19
+ else if (contract.expressionMode === "expansive") {
20
+ budget = 640;
21
+ }
22
+ else {
23
+ budget = 256;
24
+ }
25
+ if (contract.maxSentences <= 1)
26
+ budget = Math.min(budget, 96);
27
+ else if (contract.maxSentences === 2)
28
+ budget = Math.min(budget, 160);
29
+ else if (contract.maxSentences === 3)
30
+ budget = Math.min(budget, 320);
31
+ if (contract.initiativeMode === "reactive") {
32
+ budget = clampInt(budget * 0.85, 64, 1024);
33
+ }
34
+ return budget;
35
+ }
36
+ export function deriveGenerationControls(input, existingMaxTokens) {
37
+ const recommendedMax = estimateMaxTokens(input.responseContract);
38
+ const maxTokens = recommendedMax !== undefined
39
+ ? existingMaxTokens !== undefined
40
+ ? Math.min(existingMaxTokens, recommendedMax)
41
+ : recommendedMax
42
+ : existingMaxTokens;
43
+ return {
44
+ maxTokens,
45
+ requireConfirmation: input.policyModifiers?.requireConfirmation
46
+ ?? (input.responseContract?.boundaryMode === "confirm-first"),
47
+ };
48
+ }
package/dist/index.d.ts CHANGED
@@ -2,8 +2,8 @@ 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, 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, } from "./types.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, 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
7
  export { computeSelfReflection, computeEmotionalTendency, buildSelfReflectionContext } from "./self-recognition.js";
8
8
  export type { SelfReflection } from "./self-recognition.js";
9
9
  export { PsycheInteraction } from "./interaction.js";
@@ -23,6 +23,11 @@ export { assessMetacognition, computeEmotionalConfidence, generateRegulationSugg
23
23
  export type { MetacognitiveAssessment, RegulationSuggestion, DetectedDefense } from "./metacognition.js";
24
24
  export { computeDecisionBias, computeAttentionWeights, computeExploreExploit, buildDecisionContext, computePolicyModifiers, buildPolicyContext, } from "./decision-bias.js";
25
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 { computeAppraisalAxes, mergeAppraisalResidue, getResidueIntensity } from "./appraisal.js";
30
+ export { computeRelationMove, evolveDyadicField, evolvePendingRelationSignals, getLoopPressure } from "./relation-dynamics.js";
26
31
  export { computeExperientialField, computeCoherence, detectUnnamedEmotion, computeAffectCore } from "./experiential-field.js";
27
32
  export type { ExperientialField, ExperientialQuality, ConstructionContext } from "./experiential-field.js";
28
33
  export { computeGenerativeSelf, predictSelfReaction, detectInternalConflicts, buildIdentityNarrative } from "./generative-self.js";
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@
12
12
  export { PsycheEngine } from "./core.js";
13
13
  // Storage
14
14
  export { FileStorageAdapter, MemoryStorageAdapter } from "./storage.js";
15
- 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, } from "./types.js";
15
+ 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";
16
16
  // Self-recognition
17
17
  export { computeSelfReflection, computeEmotionalTendency, buildSelfReflectionContext } from "./self-recognition.js";
18
18
  // Multi-agent interaction
@@ -33,6 +33,12 @@ export { updateAttachment, computeSeparationEffect, computeReunionEffect, } from
33
33
  export { assessMetacognition, computeEmotionalConfidence, generateRegulationSuggestions, detectDefenseMechanisms, } from "./metacognition.js";
34
34
  // Decision bias (P5) + PolicyModifiers (v9)
35
35
  export { computeDecisionBias, computeAttentionWeights, computeExploreExploit, buildDecisionContext, computePolicyModifiers, buildPolicyContext, } from "./decision-bias.js";
36
+ // Subjectivity kernel (v9.3)
37
+ export { computeSubjectivityKernel, buildSubjectivityContext } from "./subjectivity.js";
38
+ export { computeResponseContract, buildResponseContractContext } from "./response-contract.js";
39
+ export { deriveGenerationControls } from "./host-controls.js";
40
+ export { computeAppraisalAxes, mergeAppraisalResidue, getResidueIntensity } from "./appraisal.js";
41
+ export { computeRelationMove, evolveDyadicField, evolvePendingRelationSignals, getLoopPressure } from "./relation-dynamics.js";
36
42
  // Experiential field (P6 + P8 Barrett construction)
37
43
  export { computeExperientialField, computeCoherence, detectUnnamedEmotion, computeAffectCore } from "./experiential-field.js";
38
44
  // Generative self (P6)
package/dist/prompt.d.ts CHANGED
@@ -15,6 +15,8 @@ export declare function buildDynamicContext(state: PsycheState, userId?: string,
15
15
  autonomicDescription?: string;
16
16
  autonomicState?: AutonomicState;
17
17
  primarySystemsDescription?: string;
18
+ subjectivityContext?: string;
19
+ responseContractContext?: string;
18
20
  policyContext?: string;
19
21
  }): string;
20
22
  /**
@@ -68,5 +70,7 @@ export declare function buildCompactContext(state: PsycheState, userId?: string,
68
70
  autonomicDescription?: string;
69
71
  autonomicState?: AutonomicState;
70
72
  primarySystemsDescription?: string;
73
+ subjectivityContext?: string;
74
+ responseContractContext?: string;
71
75
  policyContext?: string;
72
76
  }): string;
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
+ };