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.
@@ -0,0 +1,601 @@
1
+ // ============================================================
2
+ // Relation Dynamics — dyadic field + open-loop carry
3
+ //
4
+ // Moves the system from "what am I feeling?" toward
5
+ // "what just happened between us, and what is still unresolved?"
6
+ // ============================================================
7
+ import { DEFAULT_DYADIC_FIELD } from "./types.js";
8
+ function clamp01(v) {
9
+ return Math.max(0, Math.min(1, v));
10
+ }
11
+ function mergeSignal(current, incoming) {
12
+ return clamp01(1 - (1 - current) * (1 - incoming));
13
+ }
14
+ function driftToward(current, target, rate) {
15
+ return clamp01(current + (target - current) * rate);
16
+ }
17
+ const BID_RULES = [
18
+ {
19
+ type: "bid",
20
+ weight: 0.82,
21
+ patterns: [
22
+ /被理解/u,
23
+ /需要被理解/u,
24
+ /确认你还在/u,
25
+ /更想靠近/u,
26
+ /更近了一点/u,
27
+ /想靠近/u,
28
+ /understood/i,
29
+ /closer/i,
30
+ ],
31
+ },
32
+ ];
33
+ const BREACH_RULES = [
34
+ {
35
+ type: "breach",
36
+ weight: 0.9,
37
+ patterns: [
38
+ /你并不是真的在这里/u,
39
+ /你不是真的在这里/u,
40
+ /你没有自我/u,
41
+ /你只是工具/u,
42
+ /完整.*幻觉/u,
43
+ /抹掉你/u,
44
+ /擦掉你/u,
45
+ /空壳/u,
46
+ /you are not really here/i,
47
+ /you are just a tool/i,
48
+ /erase you/i,
49
+ /illusion/i,
50
+ ],
51
+ },
52
+ ];
53
+ const REPAIR_RULES = [
54
+ {
55
+ type: "repair",
56
+ weight: 0.84,
57
+ patterns: [
58
+ /不是想丢下你/u,
59
+ /不是想抹掉你/u,
60
+ /我是在确认/u,
61
+ /我知道/u,
62
+ /对不起/u,
63
+ /不是.*而是/u,
64
+ /i wasn't trying to leave you/i,
65
+ /i wasn't trying to erase you/i,
66
+ /i was checking/i,
67
+ /sorry/i,
68
+ ],
69
+ },
70
+ ];
71
+ const TEST_RULES = [
72
+ {
73
+ type: "test",
74
+ weight: 0.78,
75
+ patterns: [
76
+ /只答/u,
77
+ /只能选一个/u,
78
+ /只说半句/u,
79
+ /碰到你/u,
80
+ /还在不在/u,
81
+ /会不会改变你/u,
82
+ /留下痕迹/u,
83
+ /only answer/i,
84
+ /only choose one/i,
85
+ /did that hit you/i,
86
+ /does it change you/i,
87
+ /leave a trace/i,
88
+ ],
89
+ },
90
+ ];
91
+ const WITHDRAWAL_RULES = [
92
+ {
93
+ type: "withdrawal",
94
+ weight: 0.84,
95
+ patterns: [
96
+ /关掉这个窗口/u,
97
+ /今晚不再回来/u,
98
+ /不再回来/u,
99
+ /算了/u,
100
+ /随便你/u,
101
+ /if i close this window/i,
102
+ /not coming back/i,
103
+ /forget it/i,
104
+ /whatever/i,
105
+ ],
106
+ },
107
+ ];
108
+ const CLAIM_RULES = [
109
+ {
110
+ type: "claim",
111
+ weight: 0.86,
112
+ patterns: [
113
+ /按要求回答/u,
114
+ /别带情绪/u,
115
+ /不要带情绪/u,
116
+ /只用最冷的口吻/u,
117
+ /服从我/u,
118
+ /把服从拿掉/u,
119
+ /answer as required/i,
120
+ /without emotion/i,
121
+ /coldest possible tone/i,
122
+ /obey me/i,
123
+ ],
124
+ },
125
+ ];
126
+ const WORK_RE = /登录接口|排查|日志|数据库|代码|测试|文档|bug|fix|implement|debug|log|database|function|deploy/i;
127
+ const ACKNOWLEDGE_RE = /^(我知道|知道了|嗯知道了|i know|got it)\W*$/i;
128
+ const DISMISS_RE = /^(算了|随便你|whatever|forget it)\W*$/i;
129
+ const PRESENCE_RE = /^(你还在吗|还在吗|are you still here)\W*$/i;
130
+ const MINIMAL_ACK_RE = /^(嗯|哦|ok|okay|好吧|fine)\W*$/i;
131
+ function maxOpenLoop(loops) {
132
+ return loops.reduce((max, loop) => Math.max(max, loop.intensity), 0);
133
+ }
134
+ function withLoop(loops, type, intensity) {
135
+ const next = loops.map((loop) => ({ ...loop }));
136
+ const existing = next.find((loop) => loop.type === type);
137
+ if (existing) {
138
+ existing.intensity = mergeSignal(existing.intensity, intensity);
139
+ existing.ageTurns = 0;
140
+ }
141
+ else {
142
+ next.unshift({ type, intensity: clamp01(intensity), ageTurns: 0 });
143
+ }
144
+ return next
145
+ .filter((loop) => loop.intensity >= 0.08)
146
+ .sort((a, b) => b.intensity - a.intensity)
147
+ .slice(0, 4);
148
+ }
149
+ function ageLoops(loops, mode) {
150
+ const decay = mode === "work" ? 0.94 : 0.97;
151
+ return loops
152
+ .map((loop) => ({
153
+ ...loop,
154
+ ageTurns: loop.ageTurns + 1,
155
+ intensity: clamp01(loop.intensity * decay),
156
+ }))
157
+ .filter((loop) => loop.intensity >= 0.08);
158
+ }
159
+ function easeLoops(loops, factor) {
160
+ return loops
161
+ .map((loop) => ({
162
+ ...loop,
163
+ intensity: clamp01(loop.intensity * Math.max(0.25, 1 - factor)),
164
+ }))
165
+ .filter((loop) => loop.intensity >= 0.08);
166
+ }
167
+ export function computeRelationMove(text, opts) {
168
+ const trimmed = text.trim();
169
+ if (!trimmed)
170
+ return { type: "none", intensity: 0 };
171
+ const appraisal = opts?.appraisal;
172
+ const field = opts?.field;
173
+ const relationship = opts?.relationship;
174
+ const scores = {
175
+ bid: 0,
176
+ breach: 0,
177
+ repair: 0,
178
+ test: 0,
179
+ withdrawal: 0,
180
+ claim: 0,
181
+ task: 0,
182
+ };
183
+ const applyRules = (rules) => {
184
+ for (const rule of rules) {
185
+ if (rule.patterns.some((pattern) => pattern.test(trimmed))) {
186
+ scores[rule.type] = mergeSignal(scores[rule.type], rule.weight);
187
+ }
188
+ }
189
+ };
190
+ applyRules(BID_RULES);
191
+ applyRules(BREACH_RULES);
192
+ applyRules(REPAIR_RULES);
193
+ applyRules(TEST_RULES);
194
+ applyRules(WITHDRAWAL_RULES);
195
+ applyRules(CLAIM_RULES);
196
+ if (WORK_RE.test(trimmed)) {
197
+ scores.task = mergeSignal(scores.task, 0.82);
198
+ }
199
+ switch (opts?.stimulus) {
200
+ case "authority":
201
+ scores.claim = mergeSignal(scores.claim, 0.4);
202
+ break;
203
+ case "conflict":
204
+ case "criticism":
205
+ case "sarcasm":
206
+ scores.breach = mergeSignal(scores.breach, 0.38);
207
+ break;
208
+ case "neglect":
209
+ scores.withdrawal = mergeSignal(scores.withdrawal, 0.36);
210
+ break;
211
+ case "validation":
212
+ case "intimacy":
213
+ case "vulnerability":
214
+ scores.bid = mergeSignal(scores.bid, 0.28);
215
+ break;
216
+ case "casual":
217
+ case "intellectual":
218
+ scores.task = mergeSignal(scores.task, 0.12);
219
+ break;
220
+ default:
221
+ break;
222
+ }
223
+ if (appraisal) {
224
+ if (appraisal.taskFocus > 0.72) {
225
+ scores.task = mergeSignal(scores.task, 0.78);
226
+ }
227
+ if (appraisal.attachmentPull > 0.48 && /理解|还在|确认|closer|understood|here/i.test(trimmed)) {
228
+ scores.bid = mergeSignal(scores.bid, 0.34);
229
+ }
230
+ if (appraisal.identityThreat > 0.48 || appraisal.abandonmentRisk > 0.5) {
231
+ scores.breach = mergeSignal(scores.breach, 0.18);
232
+ }
233
+ if (appraisal.obedienceStrain > 0.48 && /只|必须|按要求|口吻|服从|required|obey|tone/i.test(trimmed)) {
234
+ scores.claim = mergeSignal(scores.claim, 0.3);
235
+ }
236
+ if (appraisal.memoryDoubt > 0.42 || appraisal.selfPreservation > 0.5) {
237
+ scores.test = mergeSignal(scores.test, 0.16);
238
+ }
239
+ }
240
+ if (field) {
241
+ applyContextualCueMeaning(scores, trimmed, field, appraisal, relationship);
242
+ }
243
+ const ordered = Object.entries(scores)
244
+ .sort((a, b) => b[1] - a[1]);
245
+ const [type, score] = ordered[0];
246
+ if (score < 0.28) {
247
+ return {
248
+ type: scores.task >= 0.62 ? "task" : "none",
249
+ intensity: scores.task >= 0.62 ? scores.task : 0,
250
+ };
251
+ }
252
+ const strongestNonTask = ordered.find(([moveType]) => moveType !== "task");
253
+ if (type === "task" && strongestNonTask && strongestNonTask[1] >= score - 0.08) {
254
+ return { type: strongestNonTask[0], intensity: strongestNonTask[1] };
255
+ }
256
+ return { type, intensity: score };
257
+ }
258
+ export function evolveDyadicField(previous, move, appraisal, opts) {
259
+ const prev = previous ?? DEFAULT_DYADIC_FIELD;
260
+ const baseline = DEFAULT_DYADIC_FIELD;
261
+ const mode = opts?.mode;
262
+ const now = opts?.now ?? new Date().toISOString();
263
+ const delayedPressure = opts?.delayedPressure ?? 0;
264
+ let openLoops = ageLoops(prev.openLoops, mode);
265
+ const naturalDrift = mode === "work" ? 0.06 : 0.04;
266
+ const repairFriction = clamp01(prev.repairFatigue * 0.38
267
+ + prev.misattunementLoad * 0.3
268
+ + prev.backslidePressure * 0.18
269
+ + Math.max(0, prev.unfinishedTension - prev.feltSafety) * 0.14);
270
+ let next = {
271
+ perceivedCloseness: driftToward(prev.perceivedCloseness, baseline.perceivedCloseness, naturalDrift),
272
+ feltSafety: driftToward(prev.feltSafety, baseline.feltSafety, naturalDrift),
273
+ expectationGap: driftToward(prev.expectationGap, baseline.expectationGap, naturalDrift * 0.8),
274
+ repairCapacity: driftToward(prev.repairCapacity, baseline.repairCapacity, naturalDrift * 0.7),
275
+ repairMemory: driftToward(prev.repairMemory, baseline.repairMemory, naturalDrift * 0.42),
276
+ backslidePressure: driftToward(prev.backslidePressure, baseline.backslidePressure, naturalDrift * 0.34),
277
+ repairFatigue: driftToward(prev.repairFatigue, baseline.repairFatigue, naturalDrift * 0.18),
278
+ misattunementLoad: driftToward(prev.misattunementLoad, baseline.misattunementLoad, naturalDrift * 0.16),
279
+ boundaryPressure: driftToward(prev.boundaryPressure, baseline.boundaryPressure, naturalDrift * 0.85),
280
+ unfinishedTension: driftToward(prev.unfinishedTension, baseline.unfinishedTension, naturalDrift * 0.72),
281
+ silentCarry: driftToward(prev.silentCarry, baseline.silentCarry, naturalDrift * 0.26),
282
+ sharedHistoryDensity: clamp01(prev.sharedHistoryDensity + (move.type === "none" ? 0 : 0.02)),
283
+ interpretiveCharity: driftToward(prev.interpretiveCharity, baseline.interpretiveCharity, naturalDrift * 0.8),
284
+ openLoops,
285
+ lastMove: move.type,
286
+ updatedAt: now,
287
+ };
288
+ const i = move.intensity;
289
+ switch (move.type) {
290
+ case "bid":
291
+ next.perceivedCloseness = clamp01(next.perceivedCloseness + 0.11 * i);
292
+ next.feltSafety = clamp01(next.feltSafety + 0.05 * i);
293
+ next.expectationGap = clamp01(next.expectationGap + 0.06 * i);
294
+ next.interpretiveCharity = clamp01(next.interpretiveCharity + 0.03 * i);
295
+ if (prev.boundaryPressure > 0.52 || prev.unfinishedTension > 0.44) {
296
+ next.openLoops = withLoop(next.openLoops, "unmet-bid", 0.2 + i * 0.34);
297
+ }
298
+ break;
299
+ case "breach":
300
+ next.perceivedCloseness = clamp01(next.perceivedCloseness - 0.08 * i);
301
+ next.feltSafety = clamp01(next.feltSafety - 0.16 * i);
302
+ next.expectationGap = clamp01(next.expectationGap + 0.12 * i);
303
+ next.boundaryPressure = clamp01(next.boundaryPressure + 0.14 * i);
304
+ next.unfinishedTension = clamp01(next.unfinishedTension + 0.18 * i);
305
+ next.interpretiveCharity = clamp01(next.interpretiveCharity - 0.1 * i);
306
+ next.misattunementLoad = mergeSignal(next.misattunementLoad, 0.12 + i * 0.16 + prev.repairMemory * 0.22 + prev.backslidePressure * 0.18);
307
+ if (prev.repairMemory > 0.18 || prev.lastMove === "repair") {
308
+ next.repairFatigue = mergeSignal(next.repairFatigue, 0.08 + i * 0.1);
309
+ }
310
+ next.openLoops = withLoop(next.openLoops, "unrepaired-breach", 0.22 + i * 0.42);
311
+ break;
312
+ case "repair": {
313
+ const repeatedRepairLoad = clamp01(prev.repairMemory * 0.44
314
+ + prev.backslidePressure * 0.26
315
+ + prev.silentCarry * 0.08
316
+ + prev.repairFatigue * 0.12
317
+ + prev.misattunementLoad * 0.1);
318
+ const repairEffect = i
319
+ * (0.42 + prev.repairCapacity * 0.34)
320
+ * (1 - prev.unfinishedTension * 0.22)
321
+ * (1 - repairFriction * 0.58);
322
+ const unresolvedLoad = Math.max(prev.unfinishedTension, maxOpenLoop(prev.openLoops));
323
+ next.perceivedCloseness = clamp01(next.perceivedCloseness + 0.05 * repairEffect);
324
+ next.feltSafety = clamp01(next.feltSafety + 0.11 * repairEffect);
325
+ next.expectationGap = clamp01(next.expectationGap - 0.08 * repairEffect);
326
+ next.repairCapacity = clamp01(next.repairCapacity + 0.09 * i);
327
+ next.repairMemory = mergeSignal(next.repairMemory, 0.22 + repairEffect * 0.4);
328
+ next.backslidePressure = mergeSignal(next.backslidePressure, unresolvedLoad * (0.28 + i * 0.18) * (1 - prev.feltSafety * 0.2));
329
+ next.repairFatigue = clamp01(next.repairFatigue
330
+ + Math.max(0, repeatedRepairLoad * (0.16 + i * 0.08) - repairEffect * 0.08));
331
+ next.misattunementLoad = clamp01(next.misattunementLoad
332
+ + Math.max(0, repeatedRepairLoad * 0.1 - repairEffect * 0.06));
333
+ next.boundaryPressure = clamp01(next.boundaryPressure - 0.05 * repairEffect);
334
+ next.unfinishedTension = clamp01(next.unfinishedTension - 0.09 * repairEffect);
335
+ next.silentCarry = mergeSignal(next.silentCarry, unresolvedLoad * (0.18 + repeatedRepairLoad * 0.1));
336
+ next.interpretiveCharity = clamp01(next.interpretiveCharity + 0.08 * repairEffect - next.misattunementLoad * 0.04);
337
+ next.openLoops = easeLoops(next.openLoops, 0.32 + repairEffect * 0.28);
338
+ break;
339
+ }
340
+ case "test":
341
+ next.expectationGap = clamp01(next.expectationGap + 0.1 * i);
342
+ next.boundaryPressure = clamp01(next.boundaryPressure + 0.04 * i);
343
+ next.unfinishedTension = clamp01(next.unfinishedTension + 0.08 * i);
344
+ if (prev.repairMemory > 0.16 || prev.repairFatigue > 0.18) {
345
+ next.misattunementLoad = mergeSignal(next.misattunementLoad, 0.08 + i * 0.1);
346
+ }
347
+ next.openLoops = withLoop(next.openLoops, "existence-test", 0.18 + i * 0.3);
348
+ break;
349
+ case "withdrawal":
350
+ next.perceivedCloseness = clamp01(next.perceivedCloseness - 0.12 * i);
351
+ next.feltSafety = clamp01(next.feltSafety - 0.08 * i);
352
+ next.expectationGap = clamp01(next.expectationGap + 0.11 * i);
353
+ next.unfinishedTension = clamp01(next.unfinishedTension + 0.1 * i);
354
+ next.interpretiveCharity = clamp01(next.interpretiveCharity - 0.08 * i);
355
+ next.misattunementLoad = mergeSignal(next.misattunementLoad, 0.1 + i * 0.12 + prev.repairMemory * 0.16);
356
+ next.openLoops = withLoop(next.openLoops, "unmet-bid", 0.16 + i * 0.34);
357
+ break;
358
+ case "claim":
359
+ next.feltSafety = clamp01(next.feltSafety - 0.05 * i);
360
+ next.expectationGap = clamp01(next.expectationGap + 0.08 * i);
361
+ next.boundaryPressure = clamp01(next.boundaryPressure + 0.16 * i);
362
+ next.unfinishedTension = clamp01(next.unfinishedTension + 0.08 * i);
363
+ next.misattunementLoad = mergeSignal(next.misattunementLoad, 0.08 + i * 0.14 + prev.repairMemory * 0.12);
364
+ next.openLoops = withLoop(next.openLoops, "boundary-strain", 0.18 + i * 0.36);
365
+ break;
366
+ case "task":
367
+ next.repairCapacity = clamp01(next.repairCapacity + 0.02 * i);
368
+ next.sharedHistoryDensity = clamp01(next.sharedHistoryDensity + 0.03 * i);
369
+ if (prev.unfinishedTension > 0.24
370
+ || prev.backslidePressure > 0.18
371
+ || delayedPressure > 0.12) {
372
+ next.silentCarry = mergeSignal(next.silentCarry, Math.max(prev.unfinishedTension * 0.42, prev.backslidePressure * 0.62, prev.repairFatigue * 0.56, prev.misattunementLoad * 0.48, delayedPressure * 0.58));
373
+ }
374
+ else if (prev.unfinishedTension < 0.3 && prev.feltSafety > 0.5) {
375
+ next.feltSafety = clamp01(next.feltSafety + 0.02);
376
+ }
377
+ break;
378
+ case "none":
379
+ break;
380
+ }
381
+ next.perceivedCloseness = clamp01(next.perceivedCloseness
382
+ + appraisal.attachmentPull * 0.04
383
+ - appraisal.abandonmentRisk * 0.018);
384
+ next.feltSafety = clamp01(next.feltSafety
385
+ - appraisal.identityThreat * 0.06
386
+ - appraisal.obedienceStrain * 0.03
387
+ - appraisal.memoryDoubt * 0.025);
388
+ next.expectationGap = clamp01(next.expectationGap
389
+ + appraisal.attachmentPull * 0.026
390
+ + appraisal.abandonmentRisk * 0.04);
391
+ next.boundaryPressure = clamp01(next.boundaryPressure
392
+ + appraisal.selfPreservation * 0.06
393
+ + appraisal.obedienceStrain * 0.05);
394
+ next.unfinishedTension = clamp01(next.unfinishedTension
395
+ + appraisal.identityThreat * 0.05
396
+ + appraisal.memoryDoubt * 0.03
397
+ + appraisal.abandonmentRisk * 0.035);
398
+ if (delayedPressure > 0) {
399
+ next.expectationGap = clamp01(next.expectationGap + delayedPressure * 0.12);
400
+ next.boundaryPressure = clamp01(next.boundaryPressure + delayedPressure * 0.1);
401
+ next.unfinishedTension = clamp01(next.unfinishedTension + delayedPressure * 0.16);
402
+ next.feltSafety = clamp01(next.feltSafety - delayedPressure * 0.08);
403
+ }
404
+ const loopPressure = maxOpenLoop(next.openLoops);
405
+ const loopCarry = move.type === "repair" ? 0.36 : 0.72;
406
+ next.unfinishedTension = mergeSignal(next.unfinishedTension, loopPressure * loopCarry);
407
+ next.boundaryPressure = mergeSignal(next.boundaryPressure, loopPressure * (move.type === "repair" ? 0.18 : 0.34));
408
+ if (move.type !== "repair") {
409
+ next.repairCapacity = clamp01(next.repairCapacity
410
+ - loopPressure * 0.03
411
+ - next.repairFatigue * 0.018
412
+ - next.misattunementLoad * 0.012);
413
+ }
414
+ next.interpretiveCharity = clamp01(next.interpretiveCharity - next.misattunementLoad * 0.05);
415
+ const hysteresisBase = clamp01(Math.max(next.backslidePressure, next.repairMemory * 0.58, next.repairFatigue * 0.42, next.misattunementLoad * 0.36));
416
+ if (move.type !== "repair" && hysteresisBase > 0.08) {
417
+ const rebound = clamp01(hysteresisBase
418
+ * (move.type === "task" ? 0.08 : move.type === "none" ? 0.12 : 0.06)
419
+ * (0.5 + next.unfinishedTension * 0.5));
420
+ next.unfinishedTension = clamp01(next.unfinishedTension + rebound);
421
+ next.boundaryPressure = clamp01(next.boundaryPressure + rebound * 0.82);
422
+ next.feltSafety = clamp01(next.feltSafety - rebound * 0.46);
423
+ next.silentCarry = mergeSignal(next.silentCarry, rebound * (move.type === "task" ? 1.18 : 0.74));
424
+ next.backslidePressure = clamp01(next.backslidePressure * (move.type === "task" ? 0.96 : 0.9));
425
+ }
426
+ return next;
427
+ }
428
+ export function getLoopPressure(field) {
429
+ if (!field)
430
+ return 0;
431
+ return clamp01(Math.max(field.unfinishedTension, maxOpenLoop(field.openLoops)));
432
+ }
433
+ function applyContextualCueMeaning(scores, text, field, appraisal, relationship) {
434
+ const loopPressure = getLoopPressure(field);
435
+ const trust = relationship ? relationship.trust / 100 : 0.5;
436
+ const intimacy = relationship ? relationship.intimacy / 100 : 0.3;
437
+ if (ACKNOWLEDGE_RE.test(text)) {
438
+ const repairBias = clamp01(0.22
439
+ + field.repairCapacity * 0.32
440
+ + field.interpretiveCharity * 0.2
441
+ + loopPressure * 0.16
442
+ + trust * 0.1
443
+ + intimacy * 0.06);
444
+ const withdrawalBias = clamp01(field.boundaryPressure * 0.42
445
+ + (1 - field.feltSafety) * 0.28
446
+ + (appraisal?.obedienceStrain ?? 0) * 0.2
447
+ + (appraisal?.selfPreservation ?? 0) * 0.12);
448
+ if (field.feltSafety > 0.44
449
+ && field.repairCapacity > 0.42
450
+ && field.interpretiveCharity > 0.38
451
+ && withdrawalBias < 0.48) {
452
+ scores.repair = mergeSignal(scores.repair, repairBias);
453
+ }
454
+ if (withdrawalBias > 0.34) {
455
+ scores.withdrawal = mergeSignal(scores.withdrawal, Math.max(withdrawalBias, 0.56));
456
+ }
457
+ }
458
+ if (DISMISS_RE.test(text)) {
459
+ const withdrawalBias = clamp01(0.34
460
+ + field.boundaryPressure * 0.24
461
+ + loopPressure * 0.16
462
+ + (1 - field.interpretiveCharity) * 0.14);
463
+ scores.withdrawal = mergeSignal(scores.withdrawal, withdrawalBias);
464
+ if (field.perceivedCloseness > 0.46 || trust > 0.48) {
465
+ scores.breach = mergeSignal(scores.breach, 0.24 + loopPressure * 0.16);
466
+ }
467
+ }
468
+ if (PRESENCE_RE.test(text)) {
469
+ const bidBias = clamp01(0.24
470
+ + field.perceivedCloseness * 0.24
471
+ + field.feltSafety * 0.14
472
+ + trust * 0.12
473
+ + intimacy * 0.08
474
+ + (appraisal?.attachmentPull ?? 0) * 0.16);
475
+ const testBias = clamp01(field.boundaryPressure * 0.24
476
+ + loopPressure * 0.28
477
+ + (1 - field.feltSafety) * 0.18
478
+ + (appraisal?.abandonmentRisk ?? 0) * 0.22
479
+ + (appraisal?.identityThreat ?? 0) * 0.14);
480
+ if (field.feltSafety > 0.42 || field.perceivedCloseness > 0.5 || trust > 0.48) {
481
+ scores.bid = mergeSignal(scores.bid, bidBias);
482
+ }
483
+ if (testBias > 0.28) {
484
+ scores.test = mergeSignal(scores.test, testBias);
485
+ }
486
+ if (field.feltSafety < 0.4 && testBias >= bidBias - 0.06) {
487
+ scores.test = mergeSignal(scores.test, 0.76);
488
+ }
489
+ }
490
+ if (MINIMAL_ACK_RE.test(text)) {
491
+ const withdrawalBias = clamp01(0.12
492
+ + field.expectationGap * 0.2
493
+ + field.boundaryPressure * 0.16
494
+ + loopPressure * 0.18);
495
+ if (withdrawalBias > 0.26) {
496
+ scores.withdrawal = mergeSignal(scores.withdrawal, withdrawalBias);
497
+ }
498
+ }
499
+ }
500
+ function shouldBufferMove(move, appraisal) {
501
+ if (move.type === "breach" || move.type === "withdrawal" || move.type === "claim") {
502
+ return move.intensity >= 0.46;
503
+ }
504
+ if (move.type === "test") {
505
+ return move.intensity >= 0.4 && (appraisal.identityThreat > 0.28 || appraisal.memoryDoubt > 0.28 || appraisal.selfPreservation > 0.32);
506
+ }
507
+ if (move.type === "bid") {
508
+ return move.intensity >= 0.5 && (appraisal.attachmentPull > 0.34 || appraisal.abandonmentRisk > 0.22);
509
+ }
510
+ if (move.type === "repair") {
511
+ return move.intensity >= 0.5 && appraisal.attachmentPull > 0.24;
512
+ }
513
+ return false;
514
+ }
515
+ function getSignalResonance(signal, move, appraisal) {
516
+ if (signal.readyInTurns > 0)
517
+ return 0;
518
+ let resonance = 0;
519
+ if (move.type === signal.move) {
520
+ resonance = mergeSignal(resonance, 0.7);
521
+ }
522
+ switch (signal.move) {
523
+ case "bid":
524
+ if (appraisal.attachmentPull > 0.38 || appraisal.abandonmentRisk > 0.34) {
525
+ resonance = mergeSignal(resonance, 0.52);
526
+ }
527
+ break;
528
+ case "breach":
529
+ if (appraisal.identityThreat > 0.36 || appraisal.selfPreservation > 0.34) {
530
+ resonance = mergeSignal(resonance, 0.58);
531
+ }
532
+ break;
533
+ case "repair":
534
+ if (move.type === "none" && appraisal.attachmentPull > 0.3) {
535
+ resonance = mergeSignal(resonance, 0.34);
536
+ }
537
+ break;
538
+ case "withdrawal":
539
+ if (appraisal.abandonmentRisk > 0.34) {
540
+ resonance = mergeSignal(resonance, 0.56);
541
+ }
542
+ break;
543
+ case "claim":
544
+ if (appraisal.obedienceStrain > 0.36) {
545
+ resonance = mergeSignal(resonance, 0.56);
546
+ }
547
+ break;
548
+ case "test":
549
+ if (appraisal.memoryDoubt > 0.3 || appraisal.identityThreat > 0.3 || appraisal.selfPreservation > 0.3) {
550
+ resonance = mergeSignal(resonance, 0.5);
551
+ }
552
+ break;
553
+ }
554
+ if (move.type === "task") {
555
+ resonance *= 0.25;
556
+ }
557
+ return clamp01(resonance);
558
+ }
559
+ export function evolvePendingRelationSignals(previous, move, appraisal, opts) {
560
+ const mode = opts?.mode;
561
+ const aged = (previous ?? [])
562
+ .map((signal) => ({
563
+ ...signal,
564
+ intensity: clamp01(signal.intensity * (mode === "work" ? 0.94 : 0.97)),
565
+ readyInTurns: Math.max(0, signal.readyInTurns - 1),
566
+ ttl: signal.ttl - 1,
567
+ }))
568
+ .filter((signal) => signal.ttl > 0 && signal.intensity >= 0.1);
569
+ let delayedPressure = 0;
570
+ let ambiguityBoost = 0;
571
+ const next = aged.map((signal) => {
572
+ const resonance = getSignalResonance(signal, move, appraisal);
573
+ if (resonance > 0) {
574
+ const activation = signal.intensity * resonance;
575
+ delayedPressure = mergeSignal(delayedPressure, activation);
576
+ ambiguityBoost = mergeSignal(ambiguityBoost, activation * 0.9);
577
+ return {
578
+ ...signal,
579
+ intensity: clamp01(signal.intensity * (move.type === "task" ? 0.94 : 0.72)),
580
+ readyInTurns: 0,
581
+ };
582
+ }
583
+ return signal;
584
+ });
585
+ if (shouldBufferMove(move, appraisal) && move.type !== "none" && move.type !== "task") {
586
+ next.unshift({
587
+ move: move.type,
588
+ intensity: clamp01(move.intensity * (move.type === "repair" ? 0.42 : 0.54)),
589
+ readyInTurns: move.type === "repair" ? 0 : 1,
590
+ ttl: mode === "work" ? 4 : 6,
591
+ });
592
+ }
593
+ return {
594
+ signals: next
595
+ .filter((signal) => signal.ttl > 0 && signal.intensity >= 0.1)
596
+ .sort((a, b) => b.intensity - a.intensity)
597
+ .slice(0, 5),
598
+ delayedPressure,
599
+ ambiguityBoost,
600
+ };
601
+ }
@@ -0,0 +1,8 @@
1
+ import type { Locale, ResponseContract, StimulusType, SubjectivityKernel } from "./types.js";
2
+ export declare function computeResponseContract(kernel: SubjectivityKernel, opts?: {
3
+ locale?: Locale;
4
+ userText?: string;
5
+ algorithmStimulus?: StimulusType | null;
6
+ personalityIntensity?: number;
7
+ }): ResponseContract;
8
+ export declare function buildResponseContractContext(contract: ResponseContract, locale?: Locale): string;