@linxin666/dsh-pet 0.1.20 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +18 -11
- package/README.zh.md +18 -11
- package/assets/whale/pet.json +9 -0
- package/lib/client.js +394 -58
- package/lib/client.js.map +1 -1
- package/lib/index.js +1007 -43
- package/lib/invariant.js +1 -1
- package/lib/{state-Diiq8vjk.js → state-DrMX22GL.js} +86 -28
- package/lib/types/affinity.d.ts +4 -0
- package/lib/types/affinity.d.ts.map +1 -1
- package/lib/types/affinity.js +32 -6
- package/lib/types/chatter.d.ts +111 -0
- package/lib/types/chatter.d.ts.map +1 -0
- package/lib/types/chatter.js +756 -0
- package/lib/types/client/PetDockEntry.d.ts.map +1 -1
- package/lib/types/client/PetDockEntry.js +1 -1
- package/lib/types/client/PetSprite.d.ts.map +1 -1
- package/lib/types/client/PetSprite.js +133 -16
- package/lib/types/client/PluginSettingsCard.d.ts +28 -0
- package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PluginSettingsCard.js +147 -6
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +1 -0
- package/lib/types/client/locales.d.ts +4 -0
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/locales.js +4 -0
- package/lib/types/client/sequences.d.ts +10 -0
- package/lib/types/client/sequences.d.ts.map +1 -0
- package/lib/types/client/sequences.js +20 -0
- package/lib/types/event-projection.d.ts +14 -1
- package/lib/types/event-projection.d.ts.map +1 -1
- package/lib/types/event-projection.js +38 -17
- package/lib/types/ledger.d.ts.map +1 -1
- package/lib/types/ledger.js +15 -7
- package/lib/types/loopback.d.ts +25 -0
- package/lib/types/loopback.d.ts.map +1 -0
- package/lib/types/loopback.js +68 -0
- package/lib/types/persist.d.ts.map +1 -1
- package/lib/types/persist.js +3 -1
- package/lib/types/registry.d.ts +7 -1
- package/lib/types/registry.d.ts.map +1 -1
- package/lib/types/registry.js +47 -3
- package/lib/types/remarks.d.ts +2 -0
- package/lib/types/remarks.d.ts.map +1 -1
- package/lib/types/remarks.js +35 -0
- package/lib/types/routes.d.ts.map +1 -1
- package/lib/types/routes.js +14 -0
- package/lib/types/service.d.ts +6 -0
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/service.js +15 -2
- package/lib/types/state.d.ts +7 -4
- package/lib/types/state.d.ts.map +1 -1
- package/lib/types/state.js +20 -20
- package/package.json +9 -9
- package/src/affinity.test.ts +4 -2
- package/src/affinity.ts +37 -6
- package/src/chatter.test.ts +154 -0
- package/src/chatter.ts +787 -0
- package/src/client/PetDockEntry.test.tsx +93 -0
- package/src/client/PetDockEntry.tsx +1 -0
- package/src/client/PetSprite.test.tsx +253 -3
- package/src/client/PetSprite.tsx +192 -37
- package/src/client/PluginSettingsCard.tsx +229 -18
- package/src/client/index.test.tsx +95 -0
- package/src/client/index.ts +1 -0
- package/src/client/locales.ts +4 -0
- package/src/client/pet-css.test.ts +54 -0
- package/src/client/pet.module.css +215 -57
- package/src/client/sequences.test.ts +33 -0
- package/src/client/sequences.ts +33 -0
- package/src/client/settings-card.module.css +90 -1
- package/src/event-projection.ts +49 -16
- package/src/ledger.test.ts +42 -1
- package/src/ledger.ts +15 -7
- package/src/loopback.ts +63 -0
- package/src/persist.test.ts +18 -1
- package/src/persist.ts +3 -1
- package/src/registry.test.ts +79 -4
- package/src/registry.ts +58 -6
- package/src/remarks.ts +36 -0
- package/src/routes.ts +11 -0
- package/src/service.ts +27 -2
- package/src/state.test.ts +13 -0
- package/src/state.ts +24 -18
package/lib/invariant.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as AFFINITY_MAX, n as animationForPhase, o as AFFINITY_RANKS, u as defaultAffinityConfig } from "./state-
|
|
1
|
+
import { a as AFFINITY_MAX, n as animationForPhase, o as AFFINITY_RANKS, u as defaultAffinityConfig } from "./state-DrMX22GL.js";
|
|
2
2
|
//#region src/invariant.ts
|
|
3
3
|
/**
|
|
4
4
|
* Package invariants — cheap structural checks run at import time on the
|
|
@@ -29,7 +29,13 @@ const BUILTIN_REMARKS = {
|
|
|
29
29
|
"今天的摸头也收货成功!",
|
|
30
30
|
"蹭蹭你的手心,这是回礼~",
|
|
31
31
|
"多摸摸我,亲密度会涨哦!",
|
|
32
|
-
"闭眼享受中,请勿打扰~"
|
|
32
|
+
"闭眼享受中,请勿打扰~",
|
|
33
|
+
"头再低一点,够不着了~",
|
|
34
|
+
"呼噜呼噜,声音都冒出来了",
|
|
35
|
+
"这手感,比小鱼干还上瘾",
|
|
36
|
+
"摸到第三下,满意",
|
|
37
|
+
"耳朵后面,别漏了……啊,舒服",
|
|
38
|
+
"被摸得尾巴都卷起来了"
|
|
33
39
|
],
|
|
34
40
|
petCooldown: [
|
|
35
41
|
"摸过头啦,让鲸鱼娘歇口气~",
|
|
@@ -41,7 +47,13 @@ const BUILTIN_REMARKS = {
|
|
|
41
47
|
"我知道你喜欢我,但也要节制呀~",
|
|
42
48
|
"歇一歇,摸摸的手感会更好哦~",
|
|
43
49
|
"咕……等我回个蓝~",
|
|
44
|
-
"让我先消化一下刚才的爱!"
|
|
50
|
+
"让我先消化一下刚才的爱!",
|
|
51
|
+
"痒痒的,先让我缓一下……",
|
|
52
|
+
"再摸就掉线了,真的",
|
|
53
|
+
"库存的呼噜声用完了",
|
|
54
|
+
"手歇会儿,我也要补个蓝",
|
|
55
|
+
"舒服归舒服,得缓缓呀",
|
|
56
|
+
"再摸下去,我就要融化了"
|
|
45
57
|
],
|
|
46
58
|
feed: [
|
|
47
59
|
"呜哇!小鱼干好好吃!",
|
|
@@ -53,7 +65,13 @@ const BUILTIN_REMARKS = {
|
|
|
53
65
|
"小鱼干最好吃了,再来亿条!",
|
|
54
66
|
"饱餐一顿,马上满血复活~",
|
|
55
67
|
"这个味道,是幸福的味道!",
|
|
56
|
-
"吃完了还不忘舔舔爪子~"
|
|
68
|
+
"吃完了还不忘舔舔爪子~",
|
|
69
|
+
"这小鱼干,是今天的顶配",
|
|
70
|
+
"一口下去,精神头全回来了",
|
|
71
|
+
"脆!香!就是这个味儿",
|
|
72
|
+
"边吃边摇尾巴,形象不要了",
|
|
73
|
+
"好吃到眼睛都眯起来了",
|
|
74
|
+
"这块鱼干我记住了,懂我的"
|
|
57
75
|
],
|
|
58
76
|
feedCooldown: [
|
|
59
77
|
"吃饱啦,晚点再喂~",
|
|
@@ -65,7 +83,13 @@ const BUILTIN_REMARKS = {
|
|
|
65
83
|
"先散步一圈再吃下一顿!",
|
|
66
84
|
"肚皮已经鼓鼓的啦~",
|
|
67
85
|
"好吃是好吃,可也得节制呀~",
|
|
68
|
-
"等我饿了会告诉你哦~"
|
|
86
|
+
"等我饿了会告诉你哦~",
|
|
87
|
+
"胃说它满了,脑子说还能吃",
|
|
88
|
+
"这条得留着慢慢品",
|
|
89
|
+
"先消消食,待会儿再战",
|
|
90
|
+
"塞不下了,真塞不下了",
|
|
91
|
+
"闻着香,可惜没地方放了",
|
|
92
|
+
"嗝……这顿值了"
|
|
69
93
|
],
|
|
70
94
|
noTreats: [
|
|
71
95
|
"没有小鱼干了,多陪我工作一会儿吧~",
|
|
@@ -77,7 +101,13 @@ const BUILTIN_REMARKS = {
|
|
|
77
101
|
"粮仓见底啦,用几轮任务换一条鱼干吧~",
|
|
78
102
|
"饿着肚子等你完成下一轮任务~",
|
|
79
103
|
"小鱼干藏在你的工作里,去找找看!",
|
|
80
|
-
"先工作后干饭,我们的约定哦~"
|
|
104
|
+
"先工作后干饭,我们的约定哦~",
|
|
105
|
+
"粮仓见底,全靠感情撑着了",
|
|
106
|
+
"饿是真饿,活也是真得干",
|
|
107
|
+
"画饼充饥……不对,画鱼干充饥",
|
|
108
|
+
"没鱼干的日子,靠意志力过",
|
|
109
|
+
"下一轮任务,我闻到了鱼干味",
|
|
110
|
+
"先记账上,欠我两条,记住啦"
|
|
81
111
|
]
|
|
82
112
|
};
|
|
83
113
|
/** The legacy first line of one kind (direct callers' fallback copy). */
|
|
@@ -137,6 +167,11 @@ var RemarkPicker = class {
|
|
|
137
167
|
this.counters.set(kind, index + 1);
|
|
138
168
|
return pool[index];
|
|
139
169
|
}
|
|
170
|
+
/** Select a line from a stable external counter without changing local picker state. */
|
|
171
|
+
pickAt(kind, count) {
|
|
172
|
+
const pool = this.pools[kind];
|
|
173
|
+
return pool[Math.max(0, Math.floor(count)) % pool.length];
|
|
174
|
+
}
|
|
140
175
|
};
|
|
141
176
|
//#endregion
|
|
142
177
|
//#region src/affinity.ts
|
|
@@ -220,6 +255,8 @@ function emptyAffinity() {
|
|
|
220
255
|
lastFeedAt: 0,
|
|
221
256
|
pets: 0,
|
|
222
257
|
feeds: 0,
|
|
258
|
+
petRejects: 0,
|
|
259
|
+
feedRejects: 0,
|
|
223
260
|
turns: 0
|
|
224
261
|
};
|
|
225
262
|
}
|
|
@@ -246,6 +283,11 @@ function affinityViewOf(state, nowMs, config = defaultAffinityConfig) {
|
|
|
246
283
|
function clamp(points) {
|
|
247
284
|
return Math.min(AFFINITY_MAX, Math.max(0, points));
|
|
248
285
|
}
|
|
286
|
+
/** Built-in reaction selected deterministically from a persisted counter. */
|
|
287
|
+
function countedRemark(kind, count) {
|
|
288
|
+
const pool = BUILTIN_REMARKS[kind];
|
|
289
|
+
return pool[Math.max(0, Math.floor(count)) % pool.length] ?? builtinRemark(kind);
|
|
290
|
+
}
|
|
249
291
|
/**
|
|
250
292
|
* Apply one interaction to a copy of the state (immutable style: returns a
|
|
251
293
|
* new object; the caller replaces the persisted state). Cooldowns only
|
|
@@ -255,36 +297,42 @@ function clamp(points) {
|
|
|
255
297
|
function applyInteraction(state, kind, nowMs, config = defaultAffinityConfig) {
|
|
256
298
|
const next = { ...state };
|
|
257
299
|
if (kind === "pet") {
|
|
258
|
-
if (state.lastPetAt !== 0 && nowMs - state.lastPetAt < config.petCooldownMs)
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
300
|
+
if (state.lastPetAt !== 0 && nowMs - state.lastPetAt < config.petCooldownMs) {
|
|
301
|
+
next.petRejects += 1;
|
|
302
|
+
return {
|
|
303
|
+
affinity: next,
|
|
304
|
+
delta: 0,
|
|
305
|
+
reaction: countedRemark("petCooldown", state.petRejects),
|
|
306
|
+
accepted: false
|
|
307
|
+
};
|
|
308
|
+
}
|
|
264
309
|
next.lastPetAt = nowMs;
|
|
265
310
|
next.pets += 1;
|
|
266
311
|
next.points = clamp(state.points + config.petReward);
|
|
267
312
|
return {
|
|
268
313
|
affinity: next,
|
|
269
314
|
delta: config.petReward,
|
|
270
|
-
reaction:
|
|
315
|
+
reaction: countedRemark("pet", state.pets),
|
|
271
316
|
accepted: true
|
|
272
317
|
};
|
|
273
318
|
}
|
|
274
319
|
if (kind === "feed") {
|
|
275
|
-
if (state.lastFeedAt !== 0 && nowMs - state.lastFeedAt < config.feedCooldownMs)
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
320
|
+
if (state.lastFeedAt !== 0 && nowMs - state.lastFeedAt < config.feedCooldownMs) {
|
|
321
|
+
next.feedRejects += 1;
|
|
322
|
+
return {
|
|
323
|
+
affinity: next,
|
|
324
|
+
delta: 0,
|
|
325
|
+
reaction: countedRemark("feedCooldown", state.feedRejects),
|
|
326
|
+
accepted: false
|
|
327
|
+
};
|
|
328
|
+
}
|
|
281
329
|
next.lastFeedAt = nowMs;
|
|
282
330
|
next.feeds += 1;
|
|
283
331
|
next.points = clamp(state.points + config.feedReward);
|
|
284
332
|
return {
|
|
285
333
|
affinity: next,
|
|
286
334
|
delta: config.feedReward,
|
|
287
|
-
reaction:
|
|
335
|
+
reaction: countedRemark("feed", state.feeds),
|
|
288
336
|
accepted: true
|
|
289
337
|
};
|
|
290
338
|
}
|
|
@@ -304,14 +352,17 @@ function applyTurnReward(state, config = defaultAffinityConfig) {
|
|
|
304
352
|
}
|
|
305
353
|
//#endregion
|
|
306
354
|
//#region src/state.ts
|
|
307
|
-
const defaultPetStateConfig = {
|
|
355
|
+
const defaultPetStateConfig = {
|
|
356
|
+
celebrateMs: 2400,
|
|
357
|
+
failureMs: 2400
|
|
358
|
+
};
|
|
308
359
|
/**
|
|
309
360
|
* Map one activity phase onto the animation contract.
|
|
310
361
|
* - thinking → `running` and tool → `running-right` (focused work).
|
|
311
362
|
* - review → `review` while answer text is streaming.
|
|
312
363
|
* - waiting → `waiting` (expectant pose, needs user input).
|
|
313
364
|
* - done → `jumping` (celebration), then back to `idle` after the window.
|
|
314
|
-
* - failed → `failed`
|
|
365
|
+
* - failed → `failed` briefly, then back to `idle`.
|
|
315
366
|
* - idle → `idle` (calm breathing loop).
|
|
316
367
|
*/
|
|
317
368
|
function animationForPhase(phase) {
|
|
@@ -341,26 +392,31 @@ function rowOf(animation) {
|
|
|
341
392
|
}
|
|
342
393
|
/**
|
|
343
394
|
* PetStateMachine — one instance per host process. Holds only the latest
|
|
344
|
-
* input snapshot and
|
|
395
|
+
* input snapshot and terminal-state timing; no storage, no side effects.
|
|
345
396
|
*/
|
|
346
397
|
var PetStateMachine = class {
|
|
347
|
-
config;
|
|
348
398
|
now;
|
|
349
399
|
phase = "idle";
|
|
350
400
|
line;
|
|
351
401
|
phrase;
|
|
352
402
|
sessionActive = false;
|
|
353
403
|
doneAt;
|
|
404
|
+
failedAt;
|
|
405
|
+
config;
|
|
354
406
|
constructor(config = defaultPetStateConfig, now = Date.now) {
|
|
355
|
-
this.config = config;
|
|
356
407
|
this.now = now;
|
|
408
|
+
this.config = {
|
|
409
|
+
...defaultPetStateConfig,
|
|
410
|
+
...config
|
|
411
|
+
};
|
|
357
412
|
}
|
|
358
413
|
/** Consume one projected activity update. */
|
|
359
414
|
onActivityStatus(input) {
|
|
360
415
|
this.phase = input.phase;
|
|
361
416
|
this.line = input.line;
|
|
362
417
|
this.phrase = input.phrase;
|
|
363
|
-
|
|
418
|
+
this.doneAt = input.phase === "done" ? this.now() : void 0;
|
|
419
|
+
this.failedAt = input.phase === "failed" ? this.now() : void 0;
|
|
364
420
|
}
|
|
365
421
|
/** A session became the active one (or a fresh session started). */
|
|
366
422
|
onSessionActive() {
|
|
@@ -373,14 +429,16 @@ var PetStateMachine = class {
|
|
|
373
429
|
this.line = void 0;
|
|
374
430
|
this.phrase = void 0;
|
|
375
431
|
this.doneAt = void 0;
|
|
432
|
+
this.failedAt = void 0;
|
|
376
433
|
}
|
|
377
434
|
/** Render the current animation decision. */
|
|
378
435
|
render() {
|
|
379
436
|
const nowMs = this.now();
|
|
380
437
|
let animation = animationForPhase(this.phase);
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
438
|
+
const doneSettled = this.phase === "done" && this.doneAt !== void 0 && nowMs - this.doneAt >= this.config.celebrateMs;
|
|
439
|
+
const failedSettled = this.phase === "failed" && this.failedAt !== void 0 && nowMs - this.failedAt >= this.config.failureMs;
|
|
440
|
+
if (doneSettled || failedSettled) animation = "idle";
|
|
441
|
+
const bubble = this.phase === "idle" || doneSettled || failedSettled ? void 0 : this.phrase ?? this.line;
|
|
384
442
|
return {
|
|
385
443
|
animation,
|
|
386
444
|
...bubble === void 0 ? {} : { bubble },
|
package/lib/types/affinity.d.ts
CHANGED
|
@@ -21,6 +21,10 @@ export interface AffinityState {
|
|
|
21
21
|
pets: number;
|
|
22
22
|
/** Total feed count (lifetime). */
|
|
23
23
|
feeds: number;
|
|
24
|
+
/** Total pet attempts rejected by cooldown (lifetime). */
|
|
25
|
+
petRejects: number;
|
|
26
|
+
/** Total feed attempts rejected by cooldown (lifetime). */
|
|
27
|
+
feedRejects: number;
|
|
24
28
|
/** Total completed turns witnessed (lifetime). */
|
|
25
29
|
turns: number;
|
|
26
30
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"affinity.d.ts","sourceRoot":"","sources":["../../src/affinity.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,uDAAuD;AACvD,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,CAAA;AAE3C,mCAAmC;AACnC,MAAM,WAAW,aAAa;IAC5B,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAA;IACd,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAA;IACjB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,YAAc,CAAA;AAEvC;;;;8EAI8E;AAC9E,eAAO,MAAM,cAAc;aACvB,GAAG,EAAE,CAAC;aAAE,IAAI,EAAE,IAAI;aAAE,KAAK,EAAE,GAAG;;aAC9B,GAAG,EAAE,EAAE;aAAE,IAAI,EAAE,IAAI;aAAE,KAAK,EAAE,IAAI;;aAChC,GAAG,EAAE,EAAE;aAAE,IAAI,EAAE,IAAI;aAAE,KAAK,EAAE,KAAK;;aACjC,GAAG,EAAE,EAAE;aAAE,IAAI,EAAE,MAAM;aAAE,KAAK,EAAE,MAAM;;aACpC,GAAG,EAAE,GAAG;aAAE,IAAI,EAAE,MAAM;aAAE,KAAK,EAAE,OAAO;;aACtC,GAAG,EAAE,GAAG;aAAE,IAAI,EAAE,MAAM;aAAE,KAAK,EAAE,QAAQ;;aACvC,GAAG,EAAE,IAAK;aAAE,IAAI,EAAE,MAAM;aAAE,KAAK,EAAE,SAAS;;aAC1C,GAAG,EAAE,KAAM;aAAE,IAAI,EAAE,MAAM;aAAE,KAAK,EAAE,UAAU;;aAC5C,GAAG,EAAE,MAAO;aAAE,IAAI,EAAE,MAAM;aAAE,KAAK,EAAE,WAAW;EACxC,CAAA;AAEV,+CAA+C;AAC/C,MAAM,WAAW,cAAc;IAC7B,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAA;IACjB,iCAAiC;IACjC,aAAa,EAAE,MAAM,CAAA;IACrB,uBAAuB;IACvB,UAAU,EAAE,MAAM,CAAA;IAClB,kCAAkC;IAClC,cAAc,EAAE,MAAM,CAAA;CACvB;AAED,eAAO,MAAM,qBAAqB,EAAE,cAMnC,CAAA;AAED,wBAAgB,aAAa,IAAI,aAAa,
|
|
1
|
+
{"version":3,"file":"affinity.d.ts","sourceRoot":"","sources":["../../src/affinity.ts"],"names":[],"mappings":"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"}
|
package/lib/types/affinity.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* built-in pool); the ledger layers per-pet custom remarks on top.
|
|
8
8
|
* @module @linxin666/dsh-pet/affinity
|
|
9
9
|
*/
|
|
10
|
-
import { builtinRemark } from './remarks.js';
|
|
10
|
+
import { BUILTIN_REMARKS, builtinRemark } from './remarks.js';
|
|
11
11
|
/**
|
|
12
12
|
* Affinity points cap. Historically 100; removed so long-term companions
|
|
13
13
|
* keep growing — the default limit is now the full 999,999,999 range.
|
|
@@ -37,7 +37,16 @@ export const defaultAffinityConfig = {
|
|
|
37
37
|
feedCooldownMs: 30_000,
|
|
38
38
|
};
|
|
39
39
|
export function emptyAffinity() {
|
|
40
|
-
return {
|
|
40
|
+
return {
|
|
41
|
+
points: 0,
|
|
42
|
+
lastPetAt: 0,
|
|
43
|
+
lastFeedAt: 0,
|
|
44
|
+
pets: 0,
|
|
45
|
+
feeds: 0,
|
|
46
|
+
petRejects: 0,
|
|
47
|
+
feedRejects: 0,
|
|
48
|
+
turns: 0,
|
|
49
|
+
};
|
|
41
50
|
}
|
|
42
51
|
/** Rank for a point total. */
|
|
43
52
|
export function rankOf(points) {
|
|
@@ -65,6 +74,11 @@ export function affinityViewOf(state, nowMs, config = defaultAffinityConfig) {
|
|
|
65
74
|
function clamp(points) {
|
|
66
75
|
return Math.min(AFFINITY_MAX, Math.max(0, points));
|
|
67
76
|
}
|
|
77
|
+
/** Built-in reaction selected deterministically from a persisted counter. */
|
|
78
|
+
function countedRemark(kind, count) {
|
|
79
|
+
const pool = BUILTIN_REMARKS[kind];
|
|
80
|
+
return pool[Math.max(0, Math.floor(count)) % pool.length] ?? builtinRemark(kind);
|
|
81
|
+
}
|
|
68
82
|
/**
|
|
69
83
|
* Apply one interaction to a copy of the state (immutable style: returns a
|
|
70
84
|
* new object; the caller replaces the persisted state). Cooldowns only
|
|
@@ -75,7 +89,13 @@ export function applyInteraction(state, kind, nowMs, config = defaultAffinityCon
|
|
|
75
89
|
const next = { ...state };
|
|
76
90
|
if (kind === 'pet') {
|
|
77
91
|
if (state.lastPetAt !== 0 && nowMs - state.lastPetAt < config.petCooldownMs) {
|
|
78
|
-
|
|
92
|
+
next.petRejects += 1;
|
|
93
|
+
return {
|
|
94
|
+
affinity: next,
|
|
95
|
+
delta: 0,
|
|
96
|
+
reaction: countedRemark('petCooldown', state.petRejects),
|
|
97
|
+
accepted: false,
|
|
98
|
+
};
|
|
79
99
|
}
|
|
80
100
|
next.lastPetAt = nowMs;
|
|
81
101
|
next.pets += 1;
|
|
@@ -83,13 +103,19 @@ export function applyInteraction(state, kind, nowMs, config = defaultAffinityCon
|
|
|
83
103
|
return {
|
|
84
104
|
affinity: next,
|
|
85
105
|
delta: config.petReward,
|
|
86
|
-
reaction:
|
|
106
|
+
reaction: countedRemark('pet', state.pets),
|
|
87
107
|
accepted: true,
|
|
88
108
|
};
|
|
89
109
|
}
|
|
90
110
|
if (kind === 'feed') {
|
|
91
111
|
if (state.lastFeedAt !== 0 && nowMs - state.lastFeedAt < config.feedCooldownMs) {
|
|
92
|
-
|
|
112
|
+
next.feedRejects += 1;
|
|
113
|
+
return {
|
|
114
|
+
affinity: next,
|
|
115
|
+
delta: 0,
|
|
116
|
+
reaction: countedRemark('feedCooldown', state.feedRejects),
|
|
117
|
+
accepted: false,
|
|
118
|
+
};
|
|
93
119
|
}
|
|
94
120
|
next.lastFeedAt = nowMs;
|
|
95
121
|
next.feeds += 1;
|
|
@@ -97,7 +123,7 @@ export function applyInteraction(state, kind, nowMs, config = defaultAffinityCon
|
|
|
97
123
|
return {
|
|
98
124
|
affinity: next,
|
|
99
125
|
delta: config.feedReward,
|
|
100
|
-
reaction:
|
|
126
|
+
reaction: countedRemark('feed', state.feeds),
|
|
101
127
|
accepted: true,
|
|
102
128
|
};
|
|
103
129
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pet chatter — the pet's voice while sessions work. Two speakers live here:
|
|
3
|
+
*
|
|
4
|
+
* 1. The status voice (session bubbles): big per-scene copy pools instead of
|
|
5
|
+
* one fixed line per phase, a fine-grained tool-name → copy-family map,
|
|
6
|
+
* and a compact real-argument hint ('跑跑 npm test'), in the spirit of
|
|
7
|
+
* the working-activity plugin's status line. Lines rotate round-robin —
|
|
8
|
+
* while a phase persists the copy advances every few seconds, so the pet
|
|
9
|
+
* feels alive without flickering per streamed chunk.
|
|
10
|
+
* 2. The murmur engine (碎碎念): the pet's inner whispers, woken by the
|
|
11
|
+
* model's own output — keyword moods (errors, test greens, plans,
|
|
12
|
+
* self-corrections, victories...) plus an ambient pool earned by output
|
|
13
|
+
* volume. A cooldown keeps whispers occasional.
|
|
14
|
+
*
|
|
15
|
+
* Pure and deterministic: round-robin everywhere (no Math.random), clocks are
|
|
16
|
+
* injected. The first line of each status pool is the legacy fixed copy the
|
|
17
|
+
* plugin has always shown, so existing installs keep their wording until the
|
|
18
|
+
* scene cycles. No emoji anywhere (repository rule); ~ is the whale-girl's
|
|
19
|
+
* signature.
|
|
20
|
+
* @module @linxin666/dsh-pet/chatter
|
|
21
|
+
*/
|
|
22
|
+
/** Status copy scenes — the situations a session bubble can report. */
|
|
23
|
+
export type StatusScene = 'prepare' | 'waiting' | 'thinking' | 'review' | 'toolResult' | 'done' | 'failed' | 'toolFailed' | 'maxTokens' | 'interrupted' | 'blocked';
|
|
24
|
+
/** While a scene persists, its copy advances on this cadence (ms). */
|
|
25
|
+
export declare const STATUS_ROTATE_MS = 4000;
|
|
26
|
+
/** Fixed-copy pools per status scene (first line = legacy wording). */
|
|
27
|
+
export declare const STATUS_POOLS: Readonly<Record<StatusScene, readonly string[]>>;
|
|
28
|
+
/** Tool families for friendlier per-tool status copy. */
|
|
29
|
+
export type ToolCategory = 'read' | 'write' | 'edit' | 'shell' | 'grep' | 'find' | 'ls' | 'webSearch' | 'webFetch' | 'mcp' | 'memory' | 'subagent' | 'todo' | 'browser' | 'git' | 'ask' | 'generic';
|
|
30
|
+
/** Map a raw tool name onto its copy family (working-activity style regexes). */
|
|
31
|
+
export declare function toolCategory(toolName: string): ToolCategory;
|
|
32
|
+
/**
|
|
33
|
+
* Per-family tool status pools. '{tool}' interpolates the compact tool name,
|
|
34
|
+
* '{hint}' the compact real-argument hint (both optional per line); the first
|
|
35
|
+
* entry of every pool is the legacy '正在使用 {tool}' wording.
|
|
36
|
+
*/
|
|
37
|
+
export declare const TOOL_POOLS: Readonly<Record<ToolCategory, readonly string[]>>;
|
|
38
|
+
/** Pools for the parallel-tools line; '{n}' interpolates the running count. */
|
|
39
|
+
export declare const TOOL_REMAINING_POOL: readonly string[];
|
|
40
|
+
/**
|
|
41
|
+
* A compact, human-readable hint of what a tool call actually touches —
|
|
42
|
+
* the command, the path, the pattern, the query. Best-effort parse of the
|
|
43
|
+
* raw arguments JSON; unknown shapes stay hintless. Capped short so the
|
|
44
|
+
* bubble stays compact.
|
|
45
|
+
*/
|
|
46
|
+
export declare function toolArgHint(toolName: string, argumentsJson: string): string | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* Round-robin voice for status copy. Scene-keyed picks stay STABLE while the
|
|
49
|
+
* same scene repeats (streaming chunks re-emit the same phase many times per
|
|
50
|
+
* second, and rotating per chunk would make the bubble flicker), but advance
|
|
51
|
+
* once the scene has persisted past the rotation cadence, so a long thinking
|
|
52
|
+
* stretch keeps changing its wording.
|
|
53
|
+
*/
|
|
54
|
+
export declare class StatusVoice {
|
|
55
|
+
private readonly rotateMs;
|
|
56
|
+
private readonly counters;
|
|
57
|
+
private lastScene;
|
|
58
|
+
private lastLine;
|
|
59
|
+
private lastLineAt;
|
|
60
|
+
constructor(rotateMs?: number);
|
|
61
|
+
/** Draw the next line of one pool, advancing its round-robin cursor. */
|
|
62
|
+
private draw;
|
|
63
|
+
/** Reuse the stable line or advance when the cadence elapsed. */
|
|
64
|
+
private voice;
|
|
65
|
+
/** Status line for a phase scene. */
|
|
66
|
+
scene(scene: StatusScene, nowMs: number): string;
|
|
67
|
+
/** Status line for a tool call, with the real-argument hint when known. */
|
|
68
|
+
tool(toolName: string, displayName: string, hint: string | undefined, nowMs: number): string;
|
|
69
|
+
/** Status line while sibling tools still run (always reflects the count). */
|
|
70
|
+
toolRemaining(count: number, nowMs: number): string;
|
|
71
|
+
}
|
|
72
|
+
/** One murmur trigger: keywords in the model output wake a themed pool. */
|
|
73
|
+
export interface WhisperRule {
|
|
74
|
+
/** Lowercase substrings that wake this pool (matched against chunk text). */
|
|
75
|
+
keywords: readonly string[];
|
|
76
|
+
/** Themed inner-whisper lines. */
|
|
77
|
+
pool: readonly string[];
|
|
78
|
+
}
|
|
79
|
+
/** Murmur pacing: cooldown between whispers and output volume that earns one. */
|
|
80
|
+
export declare const WHISPER_COOLDOWN_MS = 9000;
|
|
81
|
+
export declare const WHISPER_CHAR_BUDGET = 420;
|
|
82
|
+
/** How long a whisper stays on screen (host-side expiry). */
|
|
83
|
+
export declare const WHISPER_TTL_MS = 8000;
|
|
84
|
+
/** Ambient inner-whisper pool (no keyword needed; earned by output volume). */
|
|
85
|
+
export declare const WHISPER_GENERIC_POOL: readonly string[];
|
|
86
|
+
/** Keyword-triggered whisper rules, most specific moods first. */
|
|
87
|
+
export declare const WHISPER_RULES: readonly WhisperRule[];
|
|
88
|
+
/**
|
|
89
|
+
* The murmur engine (碎碎念): watches the model's own output and lets the pet
|
|
90
|
+
* whisper its inner voice. Two ways to earn a whisper:
|
|
91
|
+
* - a keyword rule matches the fresh chunk text (themed whisper);
|
|
92
|
+
* - enough output volume flowed by without one (ambient whisper).
|
|
93
|
+
* A cooldown keeps whispers occasional; all picks are round-robin so tests
|
|
94
|
+
* reproduce exact lines.
|
|
95
|
+
*/
|
|
96
|
+
export declare class WhisperEngine {
|
|
97
|
+
private readonly cooldownMs;
|
|
98
|
+
private readonly charBudget;
|
|
99
|
+
private readonly counters;
|
|
100
|
+
private genericCursor;
|
|
101
|
+
private lastWhisperAt;
|
|
102
|
+
private charsSinceWhisper;
|
|
103
|
+
constructor(cooldownMs?: number, charBudget?: number);
|
|
104
|
+
/**
|
|
105
|
+
* Feed one model-output chunk (reasoning or text). Returns the whisper to
|
|
106
|
+
* show, or undefined when the moment stays quiet.
|
|
107
|
+
*/
|
|
108
|
+
feed(text: string, nowMs: number): string | undefined;
|
|
109
|
+
private speak;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=chatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chatter.d.ts","sourceRoot":"","sources":["../../src/chatter.ts"],"names":[],"mappings":"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"}
|