@linxin666/dsh-pet 0.1.20 → 0.2.0
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 +17 -10
- package/README.zh.md +17 -10
- package/assets/whale/pet.json +9 -0
- package/lib/client.js +158 -26
- package/lib/client.js.map +1 -1
- package/lib/index.js +853 -41
- package/lib/invariant.js +1 -1
- package/lib/{state-Diiq8vjk.js → state-Ch3f4luZ.js} +51 -23
- 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 +666 -0
- package/lib/types/client/PetSprite.d.ts.map +1 -1
- package/lib/types/client/PetSprite.js +88 -14
- package/lib/types/client/PluginSettingsCard.d.ts +6 -0
- package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PluginSettingsCard.js +5 -3
- 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/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 +36 -0
- package/lib/types/remarks.d.ts +2 -0
- package/lib/types/remarks.d.ts.map +1 -1
- package/lib/types/remarks.js +5 -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 +697 -0
- package/src/client/PetSprite.test.tsx +190 -1
- package/src/client/PetSprite.tsx +112 -25
- package/src/client/PluginSettingsCard.tsx +10 -0
- package/src/client/pet-css.test.ts +42 -0
- package/src/client/pet.module.css +154 -26
- package/src/client/sequences.test.ts +33 -0
- package/src/client/sequences.ts +33 -0
- package/src/event-projection.ts +49 -16
- package/src/ledger.test.ts +42 -1
- package/src/ledger.ts +15 -7
- package/src/persist.test.ts +18 -1
- package/src/persist.ts +3 -1
- package/src/registry.test.ts +50 -3
- package/src/registry.ts +47 -3
- package/src/remarks.ts +6 -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-Ch3f4luZ.js";
|
|
2
2
|
//#region src/invariant.ts
|
|
3
3
|
/**
|
|
4
4
|
* Package invariants — cheap structural checks run at import time on the
|
|
@@ -137,6 +137,11 @@ var RemarkPicker = class {
|
|
|
137
137
|
this.counters.set(kind, index + 1);
|
|
138
138
|
return pool[index];
|
|
139
139
|
}
|
|
140
|
+
/** Select a line from a stable external counter without changing local picker state. */
|
|
141
|
+
pickAt(kind, count) {
|
|
142
|
+
const pool = this.pools[kind];
|
|
143
|
+
return pool[Math.max(0, Math.floor(count)) % pool.length];
|
|
144
|
+
}
|
|
140
145
|
};
|
|
141
146
|
//#endregion
|
|
142
147
|
//#region src/affinity.ts
|
|
@@ -220,6 +225,8 @@ function emptyAffinity() {
|
|
|
220
225
|
lastFeedAt: 0,
|
|
221
226
|
pets: 0,
|
|
222
227
|
feeds: 0,
|
|
228
|
+
petRejects: 0,
|
|
229
|
+
feedRejects: 0,
|
|
223
230
|
turns: 0
|
|
224
231
|
};
|
|
225
232
|
}
|
|
@@ -246,6 +253,11 @@ function affinityViewOf(state, nowMs, config = defaultAffinityConfig) {
|
|
|
246
253
|
function clamp(points) {
|
|
247
254
|
return Math.min(AFFINITY_MAX, Math.max(0, points));
|
|
248
255
|
}
|
|
256
|
+
/** Built-in reaction selected deterministically from a persisted counter. */
|
|
257
|
+
function countedRemark(kind, count) {
|
|
258
|
+
const pool = BUILTIN_REMARKS[kind];
|
|
259
|
+
return pool[Math.max(0, Math.floor(count)) % pool.length] ?? builtinRemark(kind);
|
|
260
|
+
}
|
|
249
261
|
/**
|
|
250
262
|
* Apply one interaction to a copy of the state (immutable style: returns a
|
|
251
263
|
* new object; the caller replaces the persisted state). Cooldowns only
|
|
@@ -255,36 +267,42 @@ function clamp(points) {
|
|
|
255
267
|
function applyInteraction(state, kind, nowMs, config = defaultAffinityConfig) {
|
|
256
268
|
const next = { ...state };
|
|
257
269
|
if (kind === "pet") {
|
|
258
|
-
if (state.lastPetAt !== 0 && nowMs - state.lastPetAt < config.petCooldownMs)
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
270
|
+
if (state.lastPetAt !== 0 && nowMs - state.lastPetAt < config.petCooldownMs) {
|
|
271
|
+
next.petRejects += 1;
|
|
272
|
+
return {
|
|
273
|
+
affinity: next,
|
|
274
|
+
delta: 0,
|
|
275
|
+
reaction: countedRemark("petCooldown", state.petRejects),
|
|
276
|
+
accepted: false
|
|
277
|
+
};
|
|
278
|
+
}
|
|
264
279
|
next.lastPetAt = nowMs;
|
|
265
280
|
next.pets += 1;
|
|
266
281
|
next.points = clamp(state.points + config.petReward);
|
|
267
282
|
return {
|
|
268
283
|
affinity: next,
|
|
269
284
|
delta: config.petReward,
|
|
270
|
-
reaction:
|
|
285
|
+
reaction: countedRemark("pet", state.pets),
|
|
271
286
|
accepted: true
|
|
272
287
|
};
|
|
273
288
|
}
|
|
274
289
|
if (kind === "feed") {
|
|
275
|
-
if (state.lastFeedAt !== 0 && nowMs - state.lastFeedAt < config.feedCooldownMs)
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
290
|
+
if (state.lastFeedAt !== 0 && nowMs - state.lastFeedAt < config.feedCooldownMs) {
|
|
291
|
+
next.feedRejects += 1;
|
|
292
|
+
return {
|
|
293
|
+
affinity: next,
|
|
294
|
+
delta: 0,
|
|
295
|
+
reaction: countedRemark("feedCooldown", state.feedRejects),
|
|
296
|
+
accepted: false
|
|
297
|
+
};
|
|
298
|
+
}
|
|
281
299
|
next.lastFeedAt = nowMs;
|
|
282
300
|
next.feeds += 1;
|
|
283
301
|
next.points = clamp(state.points + config.feedReward);
|
|
284
302
|
return {
|
|
285
303
|
affinity: next,
|
|
286
304
|
delta: config.feedReward,
|
|
287
|
-
reaction:
|
|
305
|
+
reaction: countedRemark("feed", state.feeds),
|
|
288
306
|
accepted: true
|
|
289
307
|
};
|
|
290
308
|
}
|
|
@@ -304,14 +322,17 @@ function applyTurnReward(state, config = defaultAffinityConfig) {
|
|
|
304
322
|
}
|
|
305
323
|
//#endregion
|
|
306
324
|
//#region src/state.ts
|
|
307
|
-
const defaultPetStateConfig = {
|
|
325
|
+
const defaultPetStateConfig = {
|
|
326
|
+
celebrateMs: 2400,
|
|
327
|
+
failureMs: 2400
|
|
328
|
+
};
|
|
308
329
|
/**
|
|
309
330
|
* Map one activity phase onto the animation contract.
|
|
310
331
|
* - thinking → `running` and tool → `running-right` (focused work).
|
|
311
332
|
* - review → `review` while answer text is streaming.
|
|
312
333
|
* - waiting → `waiting` (expectant pose, needs user input).
|
|
313
334
|
* - done → `jumping` (celebration), then back to `idle` after the window.
|
|
314
|
-
* - failed → `failed`
|
|
335
|
+
* - failed → `failed` briefly, then back to `idle`.
|
|
315
336
|
* - idle → `idle` (calm breathing loop).
|
|
316
337
|
*/
|
|
317
338
|
function animationForPhase(phase) {
|
|
@@ -341,26 +362,31 @@ function rowOf(animation) {
|
|
|
341
362
|
}
|
|
342
363
|
/**
|
|
343
364
|
* PetStateMachine — one instance per host process. Holds only the latest
|
|
344
|
-
* input snapshot and
|
|
365
|
+
* input snapshot and terminal-state timing; no storage, no side effects.
|
|
345
366
|
*/
|
|
346
367
|
var PetStateMachine = class {
|
|
347
|
-
config;
|
|
348
368
|
now;
|
|
349
369
|
phase = "idle";
|
|
350
370
|
line;
|
|
351
371
|
phrase;
|
|
352
372
|
sessionActive = false;
|
|
353
373
|
doneAt;
|
|
374
|
+
failedAt;
|
|
375
|
+
config;
|
|
354
376
|
constructor(config = defaultPetStateConfig, now = Date.now) {
|
|
355
|
-
this.config = config;
|
|
356
377
|
this.now = now;
|
|
378
|
+
this.config = {
|
|
379
|
+
...defaultPetStateConfig,
|
|
380
|
+
...config
|
|
381
|
+
};
|
|
357
382
|
}
|
|
358
383
|
/** Consume one projected activity update. */
|
|
359
384
|
onActivityStatus(input) {
|
|
360
385
|
this.phase = input.phase;
|
|
361
386
|
this.line = input.line;
|
|
362
387
|
this.phrase = input.phrase;
|
|
363
|
-
|
|
388
|
+
this.doneAt = input.phase === "done" ? this.now() : void 0;
|
|
389
|
+
this.failedAt = input.phase === "failed" ? this.now() : void 0;
|
|
364
390
|
}
|
|
365
391
|
/** A session became the active one (or a fresh session started). */
|
|
366
392
|
onSessionActive() {
|
|
@@ -373,14 +399,16 @@ var PetStateMachine = class {
|
|
|
373
399
|
this.line = void 0;
|
|
374
400
|
this.phrase = void 0;
|
|
375
401
|
this.doneAt = void 0;
|
|
402
|
+
this.failedAt = void 0;
|
|
376
403
|
}
|
|
377
404
|
/** Render the current animation decision. */
|
|
378
405
|
render() {
|
|
379
406
|
const nowMs = this.now();
|
|
380
407
|
let animation = animationForPhase(this.phase);
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
408
|
+
const doneSettled = this.phase === "done" && this.doneAt !== void 0 && nowMs - this.doneAt >= this.config.celebrateMs;
|
|
409
|
+
const failedSettled = this.phase === "failed" && this.failedAt !== void 0 && nowMs - this.failedAt >= this.config.failureMs;
|
|
410
|
+
if (doneSettled || failedSettled) animation = "idle";
|
|
411
|
+
const bubble = this.phase === "idle" || doneSettled || failedSettled ? void 0 : this.phrase ?? this.line;
|
|
384
412
|
return {
|
|
385
413
|
animation,
|
|
386
414
|
...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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"}
|