psyche-ai 9.2.4 → 9.2.6

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.
@@ -4,7 +4,8 @@
4
4
  // Moves the system from "what am I feeling?" toward
5
5
  // "what just happened between us, and what is still unresolved?"
6
6
  // ============================================================
7
- import { DEFAULT_DYADIC_FIELD } from "./types.js";
7
+ import { DEFAULT_DYADIC_FIELD, DEFAULT_RELATIONSHIP } from "./types.js";
8
+ import { computeAppraisalAxes, mergeAppraisalResidue } from "./appraisal.js";
8
9
  function clamp01(v) {
9
10
  return Math.max(0, Math.min(1, v));
10
11
  }
@@ -255,6 +256,72 @@ export function computeRelationMove(text, opts) {
255
256
  }
256
257
  return { type, intensity: score };
257
258
  }
259
+ export function resolveRelationContext(state, userId) {
260
+ const key = userId ?? "_default";
261
+ const relationship = state.relationships[key]
262
+ ?? state.relationships._default
263
+ ?? state.relationships[Object.keys(state.relationships)[0]]
264
+ ?? DEFAULT_RELATIONSHIP;
265
+ const field = state.dyadicFields?.[key]
266
+ ?? state.dyadicFields?._default
267
+ ?? DEFAULT_DYADIC_FIELD;
268
+ const pendingSignals = state.pendingRelationSignals?.[key]
269
+ ?? state.pendingRelationSignals?._default
270
+ ?? [];
271
+ return {
272
+ key,
273
+ relationship,
274
+ field,
275
+ pendingSignals,
276
+ };
277
+ }
278
+ export function applyRelationalTurn(state, text, opts) {
279
+ const now = opts.now ?? new Date().toISOString();
280
+ const relationContext = resolveRelationContext(state, opts.userId);
281
+ const appraisalAxes = computeAppraisalAxes(text, {
282
+ mode: opts.mode,
283
+ stimulus: opts.stimulus,
284
+ previous: state.subjectResidue?.axes,
285
+ });
286
+ const relationMove = computeRelationMove(text, {
287
+ appraisal: appraisalAxes,
288
+ stimulus: opts.stimulus,
289
+ mode: opts.mode,
290
+ field: relationContext.field,
291
+ relationship: relationContext.relationship,
292
+ });
293
+ const delayedRelation = evolvePendingRelationSignals(relationContext.pendingSignals, relationMove, appraisalAxes, { mode: opts.mode });
294
+ const field = evolveDyadicField(relationContext.field, relationMove, appraisalAxes, {
295
+ mode: opts.mode,
296
+ now,
297
+ delayedPressure: delayedRelation.delayedPressure,
298
+ });
299
+ return {
300
+ state: {
301
+ ...state,
302
+ subjectResidue: {
303
+ axes: mergeAppraisalResidue(state.subjectResidue?.axes, appraisalAxes, opts.mode),
304
+ updatedAt: now,
305
+ },
306
+ dyadicFields: {
307
+ ...(state.dyadicFields ?? {}),
308
+ [relationContext.key]: field,
309
+ },
310
+ pendingRelationSignals: {
311
+ ...(state.pendingRelationSignals ?? {}),
312
+ [relationContext.key]: delayedRelation.signals,
313
+ },
314
+ },
315
+ appraisalAxes,
316
+ relationMove,
317
+ delayedPressure: delayedRelation.delayedPressure,
318
+ relationContext: {
319
+ ...relationContext,
320
+ field,
321
+ pendingSignals: delayedRelation.signals,
322
+ },
323
+ };
324
+ }
258
325
  export function evolveDyadicField(previous, move, appraisal, opts) {
259
326
  const prev = previous ?? DEFAULT_DYADIC_FIELD;
260
327
  const baseline = DEFAULT_DYADIC_FIELD;
@@ -0,0 +1,17 @@
1
+ import type { AppraisalAxes, GenerationControls, Locale, PolicyModifiers, PsycheState, ResolvedRelationContext, ResponseContract, StimulusType, SubjectivityKernel } from "./types.js";
2
+ export interface ReplyEnvelope {
3
+ policyModifiers: PolicyModifiers;
4
+ subjectivityKernel: SubjectivityKernel;
5
+ responseContract: ResponseContract;
6
+ generationControls: GenerationControls;
7
+ policyContext: string;
8
+ subjectivityContext: string;
9
+ responseContractContext: string;
10
+ }
11
+ export declare function deriveReplyEnvelope(state: PsycheState, appraisal: AppraisalAxes, opts: {
12
+ locale: Locale;
13
+ userText?: string;
14
+ algorithmStimulus?: StimulusType | null;
15
+ personalityIntensity?: number;
16
+ relationContext?: ResolvedRelationContext;
17
+ }): ReplyEnvelope;
@@ -0,0 +1,36 @@
1
+ // ============================================================
2
+ // Reply Envelope — unified host-facing reply ABI derivation
3
+ //
4
+ // Keeps the hot path narrow by deriving reply-facing structures
5
+ // from one state snapshot and one resolved relation context.
6
+ // ============================================================
7
+ import { buildPolicyContext, computePolicyModifiers } from "./decision-bias.js";
8
+ import { deriveGenerationControls } from "./host-controls.js";
9
+ import { buildResponseContractContext, computeResponseContract } from "./response-contract.js";
10
+ import { buildSubjectivityContext, computeSubjectivityKernel } from "./subjectivity.js";
11
+ export function deriveReplyEnvelope(state, appraisal, opts) {
12
+ const policyModifiers = computePolicyModifiers(state);
13
+ const subjectivityKernel = computeSubjectivityKernel(state, policyModifiers, appraisal, opts.relationContext);
14
+ const responseContract = computeResponseContract(subjectivityKernel, {
15
+ locale: opts.locale,
16
+ userText: opts.userText,
17
+ algorithmStimulus: opts.algorithmStimulus,
18
+ personalityIntensity: opts.personalityIntensity,
19
+ });
20
+ const generationControls = deriveGenerationControls({
21
+ responseContract,
22
+ policyModifiers,
23
+ });
24
+ const policyContext = buildPolicyContext(policyModifiers, opts.locale, state.drives);
25
+ const subjectivityContext = buildSubjectivityContext(subjectivityKernel, opts.locale);
26
+ const responseContractContext = buildResponseContractContext(responseContract, opts.locale);
27
+ return {
28
+ policyModifiers,
29
+ subjectivityKernel,
30
+ responseContract,
31
+ generationControls,
32
+ policyContext,
33
+ subjectivityContext,
34
+ responseContractContext,
35
+ };
36
+ }
@@ -8,11 +8,63 @@ const EMOTIONAL_STIMULI = new Set(["vulnerability", "intimacy", "neglect"]);
8
8
  function clampInt(v, min, max) {
9
9
  return Math.max(min, Math.min(max, Math.round(v)));
10
10
  }
11
- function computeLengthBudget(locale, userText, expressionMode, kernel) {
11
+ function deriveReplyProfile(kernel) {
12
+ const taskFocused = kernel.taskPlane.focus >= 0.62;
13
+ const disciplined = kernel.taskPlane.discipline >= 0.72;
14
+ if (taskFocused && disciplined) {
15
+ return { replyProfile: "work", replyProfileBasis: "task-focus+discipline" };
16
+ }
17
+ if (taskFocused) {
18
+ return { replyProfile: "work", replyProfileBasis: "task-focus" };
19
+ }
20
+ if (disciplined) {
21
+ return { replyProfile: "work", replyProfileBasis: "discipline" };
22
+ }
23
+ return { replyProfile: "private", replyProfileBasis: "default-private" };
24
+ }
25
+ function computeLengthBudget(locale, userText, replyProfile, expressionMode, kernel) {
12
26
  const len = userText.length;
13
27
  let maxSentences = 2;
14
28
  let maxChars;
15
- if (locale === "zh") {
29
+ if (replyProfile === "work") {
30
+ if (locale === "zh") {
31
+ if (len <= 10) {
32
+ maxSentences = 2;
33
+ maxChars = 36;
34
+ }
35
+ else if (len <= 40) {
36
+ maxSentences = 3;
37
+ maxChars = clampInt(len * 2.4, 48, 220);
38
+ }
39
+ else if (len <= 120) {
40
+ maxSentences = 5;
41
+ maxChars = clampInt(len * 1.9, 120, 420);
42
+ }
43
+ else {
44
+ maxSentences = 6;
45
+ maxChars = clampInt(len * 1.5, 180, 720);
46
+ }
47
+ }
48
+ else {
49
+ if (len <= 16) {
50
+ maxSentences = 2;
51
+ maxChars = 48;
52
+ }
53
+ else if (len <= 60) {
54
+ maxSentences = 3;
55
+ maxChars = clampInt(len * 2.3, 64, 280);
56
+ }
57
+ else if (len <= 180) {
58
+ maxSentences = 5;
59
+ maxChars = clampInt(len * 1.8, 140, 520);
60
+ }
61
+ else {
62
+ maxSentences = 6;
63
+ maxChars = clampInt(len * 1.45, 220, 900);
64
+ }
65
+ }
66
+ }
67
+ else if (locale === "zh") {
16
68
  if (len <= 6) {
17
69
  maxSentences = 1;
18
70
  maxChars = 15;
@@ -49,39 +101,81 @@ function computeLengthBudget(locale, userText, expressionMode, kernel) {
49
101
  }
50
102
  }
51
103
  if (expressionMode === "brief") {
52
- maxSentences = Math.max(1, Math.min(maxSentences, 2));
53
- maxChars = maxChars !== undefined ? clampInt(maxChars * 0.85, 10, maxChars) : maxChars;
104
+ maxSentences = replyProfile === "work"
105
+ ? Math.max(2, Math.min(maxSentences, 4))
106
+ : Math.max(1, Math.min(maxSentences, 2));
107
+ maxChars = maxChars !== undefined
108
+ ? clampInt(maxChars * (replyProfile === "work" ? 0.9 : 0.85), replyProfile === "work" ? 36 : 10, maxChars)
109
+ : maxChars;
54
110
  }
55
111
  else if (expressionMode === "expansive") {
56
- maxSentences = Math.min(4, maxSentences + 1);
57
- maxChars = maxChars !== undefined ? clampInt(maxChars * 1.1, maxChars, Math.max(maxChars, 260)) : maxChars;
112
+ maxSentences = Math.min(replyProfile === "work" ? 6 : 4, maxSentences + 1);
113
+ maxChars = maxChars !== undefined
114
+ ? clampInt(maxChars * 1.1, maxChars, Math.max(maxChars, replyProfile === "work" ? 900 : 260))
115
+ : maxChars;
58
116
  }
59
117
  if (kernel?.taskPlane.focus && kernel.taskPlane.focus > 0.72) {
60
- maxSentences = Math.max(1, Math.min(maxSentences, 2));
61
- maxChars = maxChars !== undefined ? clampInt(maxChars * 0.82, 10, maxChars) : maxChars;
118
+ if (replyProfile === "work") {
119
+ maxSentences = Math.max(2, maxSentences);
120
+ maxChars = maxChars !== undefined ? clampInt(maxChars * 1.05, 48, Math.max(maxChars, 720)) : 160;
121
+ }
122
+ else {
123
+ maxSentences = Math.max(1, Math.min(maxSentences, 2));
124
+ maxChars = maxChars !== undefined ? clampInt(maxChars * 0.82, 10, maxChars) : maxChars;
125
+ }
62
126
  }
63
127
  if (kernel?.subjectPlane.guardedness && kernel.subjectPlane.guardedness > 0.72) {
64
- maxSentences = 1;
65
- maxChars = maxChars !== undefined ? clampInt(maxChars * 0.72, 8, maxChars) : 18;
128
+ if (replyProfile === "work") {
129
+ maxSentences = Math.max(2, Math.min(maxSentences, 3));
130
+ maxChars = maxChars !== undefined ? clampInt(maxChars * 0.88, 84, maxChars) : 112;
131
+ }
132
+ else {
133
+ maxSentences = 1;
134
+ maxChars = maxChars !== undefined ? clampInt(maxChars * 0.72, 8, maxChars) : 18;
135
+ }
66
136
  }
67
137
  if (kernel?.ambiguityPlane.expressionInhibition && kernel.ambiguityPlane.expressionInhibition > 0.66) {
68
- maxSentences = 1;
69
- maxChars = maxChars !== undefined ? clampInt(maxChars * 0.76, 8, maxChars) : 18;
138
+ if (replyProfile === "work") {
139
+ maxSentences = Math.max(2, Math.min(maxSentences, 3));
140
+ maxChars = maxChars !== undefined ? clampInt(maxChars * 0.9, 96, maxChars) : 136;
141
+ }
142
+ else {
143
+ maxSentences = 1;
144
+ maxChars = maxChars !== undefined ? clampInt(maxChars * 0.76, 8, maxChars) : 18;
145
+ }
70
146
  }
71
147
  if (kernel?.relationPlane.silentCarry && kernel.relationPlane.silentCarry > 0.54) {
72
- maxSentences = Math.max(1, Math.min(maxSentences, 2));
73
- maxChars = maxChars !== undefined ? clampInt(maxChars * 0.82, 8, maxChars) : 20;
148
+ maxSentences = Math.max(replyProfile === "work" ? 2 : 1, Math.min(maxSentences, replyProfile === "work" ? 3 : 2));
149
+ maxChars = maxChars !== undefined ? clampInt(maxChars * 0.82, replyProfile === "work" ? 96 : 8, maxChars) : (replyProfile === "work" ? 136 : 20);
74
150
  }
75
151
  if (kernel?.relationPlane.hysteresis && kernel.relationPlane.hysteresis > 0.64) {
76
- maxSentences = 1;
77
- maxChars = maxChars !== undefined ? clampInt(maxChars * 0.78, 8, maxChars) : 18;
152
+ if (replyProfile === "work") {
153
+ maxSentences = Math.max(2, Math.min(maxSentences, 3));
154
+ maxChars = maxChars !== undefined ? clampInt(maxChars * 0.84, 104, maxChars) : 144;
155
+ }
156
+ else {
157
+ maxSentences = 1;
158
+ maxChars = maxChars !== undefined ? clampInt(maxChars * 0.78, 8, maxChars) : 18;
159
+ }
78
160
  }
79
161
  if (kernel?.relationPlane.repairFriction && kernel.relationPlane.repairFriction > 0.62) {
80
- maxSentences = 1;
81
- maxChars = maxChars !== undefined ? clampInt(maxChars * 0.74, 8, maxChars) : 16;
162
+ if (replyProfile === "work") {
163
+ maxSentences = Math.max(2, Math.min(maxSentences, 3));
164
+ maxChars = maxChars !== undefined ? clampInt(maxChars * 0.82, 96, maxChars) : 128;
165
+ }
166
+ else {
167
+ maxSentences = 1;
168
+ maxChars = maxChars !== undefined ? clampInt(maxChars * 0.74, 8, maxChars) : 16;
169
+ }
82
170
  }
83
171
  return { maxSentences, maxChars };
84
172
  }
173
+ function buildStimulusReportingGuide(locale) {
174
+ if (locale === "zh") {
175
+ return "stimulus速记:闲聊casual/命令authority/认同validation/示弱vulnerability/冷淡neglect/批评criticism";
176
+ }
177
+ return "stimulus map: chat=casual / command=authority / agreement=validation / vulnerable=vulnerability / cold=neglect / criticism=criticism";
178
+ }
85
179
  function detectToneParticles(userText, locale) {
86
180
  if (locale !== "zh")
87
181
  return "natural";
@@ -100,9 +194,15 @@ export function computeResponseContract(kernel, opts) {
100
194
  const locale = opts?.locale ?? "zh";
101
195
  const userText = opts?.userText ?? "";
102
196
  const personalityIntensity = opts?.personalityIntensity ?? 0.7;
197
+ const { replyProfile, replyProfileBasis } = deriveReplyProfile(kernel);
103
198
  const { maxSentences, maxChars } = userText.length > 0
104
- ? computeLengthBudget(locale, userText, kernel.expressionMode, kernel)
105
- : { maxSentences: kernel.expressionMode === "brief" ? 1 : kernel.expressionMode === "expansive" ? 3 : 2, maxChars: undefined };
199
+ ? computeLengthBudget(locale, userText, replyProfile, kernel.expressionMode, kernel)
200
+ : {
201
+ maxSentences: replyProfile === "work"
202
+ ? (kernel.expressionMode === "expansive" ? 5 : 3)
203
+ : kernel.expressionMode === "brief" ? 1 : kernel.expressionMode === "expansive" ? 3 : 2,
204
+ maxChars: replyProfile === "work" ? 160 : undefined,
205
+ };
106
206
  let updateMode = "none";
107
207
  if (kernel.taskPlane.focus > 0.72) {
108
208
  updateMode = "none";
@@ -165,6 +265,8 @@ export function computeResponseContract(kernel, opts) {
165
265
  initiativeMode = "reactive";
166
266
  }
167
267
  return {
268
+ replyProfile,
269
+ replyProfileBasis,
168
270
  maxSentences,
169
271
  maxChars,
170
272
  expressionMode: kernel.expressionMode,
@@ -177,9 +279,26 @@ export function computeResponseContract(kernel, opts) {
177
279
  updateMode,
178
280
  };
179
281
  }
282
+ function describeReplyProfileBasis(basis, locale) {
283
+ if (locale === "zh") {
284
+ switch (basis) {
285
+ case "task-focus":
286
+ return "因:聚焦";
287
+ case "discipline":
288
+ return "因:纪律";
289
+ case "task-focus+discipline":
290
+ return "因:聚焦+纪律";
291
+ default:
292
+ return "因:默认私人";
293
+ }
294
+ }
295
+ return `basis:${basis}`;
296
+ }
180
297
  export function buildResponseContractContext(contract, locale = "zh") {
181
298
  if (locale === "zh") {
182
299
  const parts = [];
300
+ parts.push(contract.replyProfile === "work" ? "工作面" : "私人面");
301
+ parts.push(describeReplyProfileBasis(contract.replyProfileBasis, locale));
183
302
  const shape = contract.maxChars
184
303
  ? `${contract.maxSentences === 1 ? "1句内" : `最多${contract.maxSentences}句`},≤${contract.maxChars}字`
185
304
  : `${contract.maxSentences === 1 ? "1句内" : `最多${contract.maxSentences}句`}`;
@@ -207,14 +326,18 @@ export function buildResponseContractContext(contract, locale = "zh") {
207
326
  if (contract.emojiLimit > 0)
208
327
  parts.push(`表情≤${contract.emojiLimit}`);
209
328
  if (contract.updateMode === "stimulus")
210
- parts.push("补报stimulus");
329
+ parts.push(buildStimulusReportingGuide(locale));
211
330
  else if (contract.updateMode === "empathy")
212
331
  parts.push("对方谈感受时再报empathy");
213
- else if (contract.updateMode === "stimulus+empathy")
214
- parts.push("补报stimulus;对方谈感受时再报empathy");
332
+ else if (contract.updateMode === "stimulus+empathy") {
333
+ parts.push(buildStimulusReportingGuide(locale));
334
+ parts.push("对方谈感受时再报empathy");
335
+ }
215
336
  return `[回应契约] ${parts.join(";")}。`;
216
337
  }
217
338
  const parts = [];
339
+ parts.push(contract.replyProfile === "work" ? "work surface" : "private surface");
340
+ parts.push(describeReplyProfileBasis(contract.replyProfileBasis, locale));
218
341
  const shape = contract.maxChars
219
342
  ? `${contract.maxSentences === 1 ? "1 sentence" : `up to ${contract.maxSentences} sentences`}, <= ${contract.maxChars} chars`
220
343
  : `${contract.maxSentences === 1 ? "1 sentence" : `up to ${contract.maxSentences} sentences`}`;
@@ -240,10 +363,12 @@ export function buildResponseContractContext(contract, locale = "zh") {
240
363
  if (contract.emojiLimit > 0)
241
364
  parts.push(`emoji <= ${contract.emojiLimit}`);
242
365
  if (contract.updateMode === "stimulus")
243
- parts.push("report stimulus");
366
+ parts.push(buildStimulusReportingGuide(locale));
244
367
  else if (contract.updateMode === "empathy")
245
368
  parts.push("report empathy only when feelings are shared");
246
- else if (contract.updateMode === "stimulus+empathy")
247
- parts.push("report stimulus, and empathy only when feelings are shared");
369
+ else if (contract.updateMode === "stimulus+empathy") {
370
+ parts.push(buildStimulusReportingGuide(locale));
371
+ parts.push("report empathy only when feelings are shared");
372
+ }
248
373
  return `[Reply Contract] ${parts.join(", ")}.`;
249
374
  }
@@ -1,3 +1,3 @@
1
- import type { AppraisalAxes, Locale, PolicyModifiers, PsycheState, SubjectivityKernel } from "./types.js";
2
- export declare function computeSubjectivityKernel(state: PsycheState, policyModifiers?: PolicyModifiers, appraisal?: AppraisalAxes, userId?: string): SubjectivityKernel;
1
+ import type { AppraisalAxes, Locale, PolicyModifiers, PsycheState, SubjectivityKernel, ResolvedRelationContext } from "./types.js";
2
+ export declare function computeSubjectivityKernel(state: PsycheState, policyModifiers?: PolicyModifiers, appraisal?: AppraisalAxes, relationContextOrUserId?: ResolvedRelationContext | string): SubjectivityKernel;
3
3
  export declare function buildSubjectivityContext(kernel: SubjectivityKernel, locale?: Locale): string;
@@ -7,7 +7,7 @@
7
7
  import { DEFAULT_APPRAISAL_AXES, DEFAULT_DYADIC_FIELD, DRIVE_KEYS } from "./types.js";
8
8
  import { computeAttentionWeights, computeDecisionBias, computePolicyModifiers } from "./decision-bias.js";
9
9
  import { getResidueIntensity } from "./appraisal.js";
10
- import { getLoopPressure } from "./relation-dynamics.js";
10
+ import { getLoopPressure, resolveRelationContext } from "./relation-dynamics.js";
11
11
  function clamp01(v) {
12
12
  return Math.max(0, Math.min(1, v));
13
13
  }
@@ -47,10 +47,11 @@ function pickAttentionAnchor(state, tension, warmth) {
47
47
  candidates.sort((a, b) => b[1] - a[1]);
48
48
  return candidates[0][0];
49
49
  }
50
- function computeRelationPlane(state, appraisal, userId) {
51
- const key = userId ?? "_default";
52
- const rel = state.relationships[key] ?? state.relationships._default ?? state.relationships[Object.keys(state.relationships)[0]];
53
- const field = state.dyadicFields?.[key] ?? state.dyadicFields?._default ?? DEFAULT_DYADIC_FIELD;
50
+ function computeRelationPlane(state, appraisal, relationContext) {
51
+ const rel = relationContext?.relationship
52
+ ?? state.relationships._default
53
+ ?? state.relationships[Object.keys(state.relationships)[0]];
54
+ const field = relationContext?.field ?? DEFAULT_DYADIC_FIELD;
54
55
  const loopPressure = getLoopPressure(field);
55
56
  const closeness = wavg([
56
57
  field.perceivedCloseness,
@@ -112,9 +113,8 @@ function computeRelationPlane(state, appraisal, userId) {
112
113
  lastMove: field.lastMove,
113
114
  };
114
115
  }
115
- function computeAmbiguityPlane(state, appraisal, relationPlane, userId) {
116
- const key = userId ?? "_default";
117
- const pendingSignals = state.pendingRelationSignals?.[key] ?? state.pendingRelationSignals?._default ?? [];
116
+ function computeAmbiguityPlane(state, appraisal, relationPlane, relationContext) {
117
+ const pendingSignals = relationContext?.pendingSignals ?? [];
118
118
  const pendingPressure = clamp01(pendingSignals.reduce((sum, signal) => sum + signal.intensity * (signal.readyInTurns > 0 ? 0.55 : 0.35), 0));
119
119
  const baseConflict = wavg([
120
120
  Math.min(relationPlane.closeness, Math.max(relationPlane.loopPressure, appraisal.selfPreservation)),
@@ -219,13 +219,22 @@ function computeSubjectPlane(state, warmth, guard, appraisal, relationPlane) {
219
219
  residue: residueIntensity,
220
220
  };
221
221
  }
222
- export function computeSubjectivityKernel(state, policyModifiers = computePolicyModifiers(state), appraisal = state.subjectResidue?.axes ?? DEFAULT_APPRAISAL_AXES, userId) {
222
+ function normalizeRelationContext(state, relationContextOrUserId) {
223
+ if (!relationContextOrUserId)
224
+ return resolveRelationContext(state);
225
+ if (typeof relationContextOrUserId === "string") {
226
+ return resolveRelationContext(state, relationContextOrUserId);
227
+ }
228
+ return relationContextOrUserId;
229
+ }
230
+ export function computeSubjectivityKernel(state, policyModifiers = computePolicyModifiers(state), appraisal = state.subjectResidue?.axes ?? DEFAULT_APPRAISAL_AXES, relationContextOrUserId) {
231
+ const relationContext = normalizeRelationContext(state, relationContextOrUserId);
223
232
  const c = state.current;
224
- const rel = state.relationships[userId ?? "_default"]
233
+ const rel = relationContext?.relationship
225
234
  ?? state.relationships._default
226
235
  ?? state.relationships[Object.keys(state.relationships)[0]];
227
- const relationPlane = computeRelationPlane(state, appraisal, userId);
228
- const ambiguityPlane = computeAmbiguityPlane(state, appraisal, relationPlane, userId);
236
+ const relationPlane = computeRelationPlane(state, appraisal, relationContext);
237
+ const ambiguityPlane = computeAmbiguityPlane(state, appraisal, relationPlane, relationContext);
229
238
  const bias = computeDecisionBias(state);
230
239
  const energySignal = state.energyBudgets
231
240
  ? (norm(state.energyBudgets.attention)
@@ -25,7 +25,7 @@ export declare function predictNextStimulus(emotionalHistory: ChemicalSnapshot[]
25
25
  * High-probability positive prediction -> DA/OT micro-rise.
26
26
  * High-probability negative prediction -> CORT micro-rise.
27
27
  */
28
- export declare function generateAnticipation(predictions: StimulusPrediction[], currentChemistry: ChemicalState): AnticipationState;
28
+ export declare function generateAnticipation(predictions: StimulusPrediction[], _currentChemistry: ChemicalState): AnticipationState;
29
29
  /**
30
30
  * Compute disappointment/surprise when actual stimulus differs from prediction.
31
31
  * Returns additional chemistry delta beyond the normal stimulus response.
@@ -35,4 +35,4 @@ export declare function computeSurpriseEffect(anticipated: AnticipationState, ac
35
35
  * Evaluate if the last interaction would have gone better with different chemistry.
36
36
  * Runs a counterfactual: "what if my chemistry had been at baseline?"
37
37
  */
38
- export declare function computeRegret(preInteractionState: PsycheState, postInteractionState: PsycheState, outcomeScore: number, appliedStimulus: StimulusType | null): RegretEntry | null;
38
+ export declare function computeRegret(preInteractionState: PsycheState, postInteractionState: PsycheState, outcomeScore: number, _appliedStimulus: StimulusType | null): RegretEntry | null;
package/dist/temporal.js CHANGED
@@ -142,7 +142,7 @@ function buildPhasePrior(weights) {
142
142
  * High-probability positive prediction -> DA/OT micro-rise.
143
143
  * High-probability negative prediction -> CORT micro-rise.
144
144
  */
145
- export function generateAnticipation(predictions, currentChemistry) {
145
+ export function generateAnticipation(predictions, _currentChemistry) {
146
146
  const anticipation = {};
147
147
  for (const key of CHEMICAL_KEYS) {
148
148
  anticipation[key] = 0;
@@ -231,7 +231,7 @@ const CHEMICAL_DESCRIPTIONS = {
231
231
  * Evaluate if the last interaction would have gone better with different chemistry.
232
232
  * Runs a counterfactual: "what if my chemistry had been at baseline?"
233
233
  */
234
- export function computeRegret(preInteractionState, postInteractionState, outcomeScore, appliedStimulus) {
234
+ export function computeRegret(preInteractionState, postInteractionState, outcomeScore, _appliedStimulus) {
235
235
  // Only generate regret for bad outcomes
236
236
  if (outcomeScore >= -0.2) {
237
237
  return null;
package/dist/types.d.ts CHANGED
@@ -27,12 +27,18 @@ export declare const DRIVE_NAMES_ZH: Record<DriveType, string>;
27
27
  /** Human-readable names for each chemical */
28
28
  export declare const CHEMICAL_NAMES: Record<keyof ChemicalState, string>;
29
29
  export declare const CHEMICAL_NAMES_ZH: Record<keyof ChemicalState, string>;
30
+ export interface ChemicalRuntimeSpec {
31
+ normalMin: number;
32
+ normalMax: number;
33
+ halfLifeHours: number;
34
+ }
30
35
  /** Decay speed category */
31
36
  export type DecaySpeed = "fast" | "medium" | "slow";
32
37
  /** Decay factor per chemical (applied per hour) */
33
38
  export declare const DECAY_FACTORS: Record<DecaySpeed, number>;
34
39
  /** Which chemicals decay at which speed */
35
40
  export declare const CHEMICAL_DECAY_SPEED: Record<keyof ChemicalState, DecaySpeed>;
41
+ export declare const CHEMICAL_RUNTIME_SPECS: Record<keyof ChemicalState, ChemicalRuntimeSpec>;
36
42
  /** Psyche operating mode */
37
43
  export type PsycheMode = "natural" | "work" | "companion";
38
44
  /** Big Five personality traits (0-100 each) */
@@ -87,6 +93,8 @@ export interface ChemicalSnapshot {
87
93
  stimulus: StimulusType | null;
88
94
  dominantEmotion: string | null;
89
95
  timestamp: string;
96
+ semanticSummary?: string;
97
+ semanticPoints?: string[];
90
98
  intensity?: number;
91
99
  valence?: number;
92
100
  isCoreMemory?: boolean;
@@ -166,11 +174,30 @@ export declare const MAX_DEFENSE_PATTERNS = 10;
166
174
  export type RegulationStrategyType = "reappraisal" | "strategic-expression" | "self-soothing";
167
175
  /** Defense mechanism type */
168
176
  export type DefenseMechanismType = "rationalization" | "projection" | "sublimation" | "avoidance";
177
+ /** Which internal metric a regulation action is trying to pull back toward target */
178
+ export type RegulationTargetMetric = keyof ChemicalState | "emotional-confidence";
179
+ /** Whether the last regulation action is helping */
180
+ export type RegulationFeedbackEffect = "converging" | "holding" | "diverging";
181
+ export interface RegulationFeedback {
182
+ strategy: RegulationStrategyType;
183
+ targetMetric: RegulationTargetMetric;
184
+ effect: RegulationFeedbackEffect;
185
+ gapBefore: number;
186
+ gapNow: number;
187
+ }
169
188
  /** Record of a past regulation attempt */
170
189
  export interface RegulationRecord {
171
190
  strategy: RegulationStrategyType;
172
191
  timestamp: string;
173
192
  effective: boolean;
193
+ action?: string;
194
+ horizonTurns?: number;
195
+ remainingTurns?: number;
196
+ targetMetric?: RegulationTargetMetric;
197
+ targetValue?: number;
198
+ gapBefore?: number;
199
+ gapLatest?: number;
200
+ effect?: RegulationFeedbackEffect;
174
201
  }
175
202
  /** Tracked defense pattern frequency */
176
203
  export interface DefensePatternRecord {
@@ -185,6 +212,7 @@ export interface MetacognitiveState {
185
212
  /** Running average of emotional confidence across assessments */
186
213
  avgEmotionalConfidence: number;
187
214
  totalAssessments: number;
215
+ lastRegulationFeedback?: RegulationFeedback | null;
188
216
  }
189
217
  /** Default empty metacognitive state */
190
218
  export declare const DEFAULT_METACOGNITIVE_STATE: MetacognitiveState;
@@ -365,6 +393,13 @@ export interface DyadicFieldState {
365
393
  lastMove: RelationMoveType;
366
394
  updatedAt: string;
367
395
  }
396
+ /** Resolved per-partner view used across the hot path */
397
+ export interface ResolvedRelationContext {
398
+ key: string;
399
+ relationship: RelationshipState;
400
+ field: DyadicFieldState;
401
+ pendingSignals: PendingRelationSignalState[];
402
+ }
368
403
  export declare const DEFAULT_DYADIC_FIELD: DyadicFieldState;
369
404
  export interface AmbiguityPlaneState {
370
405
  /** How confidently the system should name what is happening */
@@ -463,6 +498,10 @@ export interface SubjectivityKernel {
463
498
  * in a compact, host-consumable form.
464
499
  */
465
500
  export interface ResponseContract {
501
+ /** Which conversational surface this turn belongs to */
502
+ replyProfile: "work" | "private";
503
+ /** Why the current turn was classified into that conversational surface */
504
+ replyProfileBasis: "task-focus" | "discipline" | "task-focus+discipline" | "default-private";
466
505
  /** Maximum suggested sentence count */
467
506
  maxSentences: number;
468
507
  /** Maximum suggested character count, when a concrete cap is available */
package/dist/types.js CHANGED
@@ -56,6 +56,17 @@ export const CHEMICAL_DECAY_SPEED = {
56
56
  NE: "fast",
57
57
  END: "fast",
58
58
  };
59
+ function decayHalfLifeHours(decayPerHour) {
60
+ return Math.log(0.5) / Math.log(decayPerHour);
61
+ }
62
+ export const CHEMICAL_RUNTIME_SPECS = {
63
+ DA: { normalMin: 35, normalMax: 75, halfLifeHours: decayHalfLifeHours(DECAY_FACTORS.medium) },
64
+ HT: { normalMin: 40, normalMax: 75, halfLifeHours: decayHalfLifeHours(DECAY_FACTORS.slow) },
65
+ CORT: { normalMin: 20, normalMax: 55, halfLifeHours: decayHalfLifeHours(DECAY_FACTORS.medium) },
66
+ OT: { normalMin: 35, normalMax: 75, halfLifeHours: decayHalfLifeHours(DECAY_FACTORS.slow) },
67
+ NE: { normalMin: 30, normalMax: 70, halfLifeHours: decayHalfLifeHours(DECAY_FACTORS.fast) },
68
+ END: { normalMin: 30, normalMax: 70, halfLifeHours: decayHalfLifeHours(DECAY_FACTORS.fast) },
69
+ };
59
70
  /** Default attachment for new relationships */
60
71
  export const DEFAULT_ATTACHMENT = {
61
72
  style: "secure",
@@ -95,6 +106,7 @@ export const DEFAULT_METACOGNITIVE_STATE = {
95
106
  defensePatterns: [],
96
107
  avgEmotionalConfidence: 0.5,
97
108
  totalAssessments: 0,
109
+ lastRegulationFeedback: null,
98
110
  };
99
111
  // ── Personhood Types (v6) ────────────────────────────────────
100
112
  /** Max causal insights to persist */
package/llms.txt CHANGED
@@ -114,5 +114,5 @@ npx psyche-mcp # Run MCP server directly
114
114
  ## Links
115
115
 
116
116
  - npm: https://www.npmjs.com/package/psyche-ai
117
- - GitHub: https://github.com/Shangri-la-0428/psyche-ai
117
+ - GitHub: https://github.com/Shangri-la-0428/oasyce_psyche
118
118
  - License: MIT