@linxin666/dsh-pet 0.1.11 → 0.1.13
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 +6 -0
- package/README.md +57 -56
- package/README.zh.md +109 -0
- package/lib/client.js +72 -54
- package/lib/client.js.map +1 -1
- package/lib/index.js +56 -35
- package/lib/invariant.js +1 -1
- package/lib/{state-IyVnKymD.js → state-C9EyycwI.js} +1 -1
- package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PluginSettingsCard.js +3 -2
- package/lib/types/client/WhalePet.d.ts.map +1 -1
- package/lib/types/client/WhalePet.js +6 -0
- package/lib/types/client/index.d.ts +13 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +2 -1
- package/lib/types/service.d.ts +12 -8
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/service.js +48 -32
- package/lib/types/state.d.ts +7 -7
- package/lib/types/state.d.ts.map +1 -1
- package/lib/types/state.js +5 -5
- package/lib/types/treats.d.ts +6 -2
- package/lib/types/treats.d.ts.map +1 -1
- package/lib/types/treats.js +14 -3
- package/package.json +2 -12
- package/src/client/PluginSettingsCard.tsx +30 -5
- package/src/client/WhalePet.test.tsx +138 -0
- package/src/client/WhalePet.tsx +5 -0
- package/src/client/index.ts +15 -2
- package/src/client/settings-card.module.css +141 -127
- package/src/service.ts +48 -36
- package/src/state.ts +7 -7
- package/src/treats.test.ts +25 -2
- package/src/treats.ts +15 -3
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as AFFINITY_MAX, c as applyTurnReward, d as rankOf, i as rowOf, l as defaultAffinityConfig, n as animationForPhase, o as AFFINITY_RANKS, r as defaultPetStateConfig, s as applyInteraction, t as PetStateMachine, u as emptyAffinity } from "./state-
|
|
1
|
+
import { a as AFFINITY_MAX, c as applyTurnReward, d as rankOf, i as rowOf, l as defaultAffinityConfig, n as animationForPhase, o as AFFINITY_RANKS, r as defaultPetStateConfig, s as applyInteraction, t as PetStateMachine, u as emptyAffinity } from "./state-C9EyycwI.js";
|
|
2
2
|
import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
3
3
|
import z from "schemastery";
|
|
4
4
|
import { Service } from "@deepseek-ai/cordis";
|
|
@@ -30,8 +30,12 @@ function cap(treats, max) {
|
|
|
30
30
|
* time output counts whole periods since the time anchor
|
|
31
31
|
* (`lastTreatGrantAt`) and advances only the time anchor. The two sources
|
|
32
32
|
* are independent so a continuously working user still earns time treats.
|
|
33
|
-
* 0 time history never backfills — the clock starts at the first settlement
|
|
34
|
-
*
|
|
33
|
+
* 0 time history never backfills — the clock starts at the first settlement,
|
|
34
|
+
* and even a zero-gain first settlement writes the time anchor so the next
|
|
35
|
+
* elapsed period can accrue (anchor deadlock fix). Both sources are clamped
|
|
36
|
+
* by the stock cap. When the anchor is already set and nothing is due, the
|
|
37
|
+
* input ledger is returned unchanged (same object), so callers can skip
|
|
38
|
+
* persistence cheaply.
|
|
35
39
|
*/
|
|
36
40
|
function settleTreatGrants(ledger, turns, nowMs, config = defaultTreatConfig) {
|
|
37
41
|
const turnDelta = Math.max(0, turns - ledger.turnsAtLastTreatGrant);
|
|
@@ -39,10 +43,19 @@ function settleTreatGrants(ledger, turns, nowMs, config = defaultTreatConfig) {
|
|
|
39
43
|
const timeAnchor = ledger.lastTreatGrantAt === 0 ? nowMs : ledger.lastTreatGrantAt;
|
|
40
44
|
const timeGrants = Math.floor(Math.max(0, nowMs - timeAnchor) / config.timeTreatMs);
|
|
41
45
|
const gained = workGrants + timeGrants;
|
|
42
|
-
if (gained <= 0)
|
|
43
|
-
ledger
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
if (gained <= 0) {
|
|
47
|
+
if (ledger.lastTreatGrantAt === 0) return {
|
|
48
|
+
ledger: {
|
|
49
|
+
...ledger,
|
|
50
|
+
lastTreatGrantAt: nowMs
|
|
51
|
+
},
|
|
52
|
+
gained: 0
|
|
53
|
+
};
|
|
54
|
+
return {
|
|
55
|
+
ledger,
|
|
56
|
+
gained: 0
|
|
57
|
+
};
|
|
58
|
+
}
|
|
46
59
|
return {
|
|
47
60
|
ledger: {
|
|
48
61
|
treats: cap(ledger.treats + gained, config.maxTreats),
|
|
@@ -162,7 +175,8 @@ var PetService = class extends Service {
|
|
|
162
175
|
treatConfig;
|
|
163
176
|
persistDir;
|
|
164
177
|
persist;
|
|
165
|
-
|
|
178
|
+
/** Completed turns already rewarded, per session (turn numbers are per-session). */
|
|
179
|
+
rewardedTurns = /* @__PURE__ */ new Map();
|
|
166
180
|
enabled;
|
|
167
181
|
disposeActivity;
|
|
168
182
|
constructor(ctx, config = {}) {
|
|
@@ -212,25 +226,31 @@ var PetService = class extends Service {
|
|
|
212
226
|
}
|
|
213
227
|
if (!this.enabled) return;
|
|
214
228
|
this.disposeActivity = (() => {
|
|
215
|
-
const disposers = [this.ctx.on("session/event", (
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
"tool"
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
229
|
+
const disposers = [this.ctx.on("session/event", (session, event) => {
|
|
230
|
+
switch (event.type) {
|
|
231
|
+
case "turn/start":
|
|
232
|
+
this.machine.onSessionActive();
|
|
233
|
+
break;
|
|
234
|
+
case "step/start":
|
|
235
|
+
this.machine.onSessionActive();
|
|
236
|
+
this.machine.onActivityStatus({ phase: "thinking" });
|
|
237
|
+
break;
|
|
238
|
+
case "tool/call":
|
|
239
|
+
this.machine.onSessionActive();
|
|
240
|
+
this.machine.onActivityStatus({
|
|
241
|
+
phase: "tool",
|
|
242
|
+
line: "tool: " + event.data.name
|
|
243
|
+
});
|
|
244
|
+
break;
|
|
245
|
+
case "turn/end":
|
|
246
|
+
this.machine.onSessionActive();
|
|
247
|
+
if (event.data.reason.kind === "completed") {
|
|
248
|
+
this.machine.onActivityStatus({ phase: "done" });
|
|
249
|
+
this.rewardTurn(String(session.id), event.data.turn);
|
|
250
|
+
} else this.machine.onActivityStatus({ phase: "idle" });
|
|
251
|
+
break;
|
|
252
|
+
default: break;
|
|
253
|
+
}
|
|
234
254
|
}), this.ctx.on("session/disposed", () => {
|
|
235
255
|
this.machine.onSessionDisposed();
|
|
236
256
|
})];
|
|
@@ -364,11 +384,10 @@ var PetService = class extends Service {
|
|
|
364
384
|
name: this.persist.name
|
|
365
385
|
}).catch(() => {});
|
|
366
386
|
}
|
|
367
|
-
/** Award the turn reward once per
|
|
368
|
-
rewardTurn() {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
this.lastTurnRewardAt = nowMs;
|
|
387
|
+
/** Award the turn reward once per completed turn (idempotent per session + turn). */
|
|
388
|
+
rewardTurn(sessionId, turn) {
|
|
389
|
+
if (turn <= (this.rewardedTurns.get(sessionId) ?? 0)) return;
|
|
390
|
+
this.rewardedTurns.set(sessionId, turn);
|
|
372
391
|
this.persist = {
|
|
373
392
|
...this.persist,
|
|
374
393
|
affinity: applyTurnReward(this.persist.affinity, this.affinityConfig)
|
|
@@ -376,12 +395,14 @@ var PetService = class extends Service {
|
|
|
376
395
|
this.flush();
|
|
377
396
|
}
|
|
378
397
|
/**
|
|
379
|
-
* Settle the treat economy (work + time output since the last
|
|
380
|
-
*
|
|
398
|
+
* Settle the treat economy (work + time output since the last settlement)
|
|
399
|
+
* and persist whenever the ledger changed. A zero-gain first settlement
|
|
400
|
+
* still starts the time clock (anchor write), which is what lets the
|
|
401
|
+
* 30-minute time output ever accrue.
|
|
381
402
|
*/
|
|
382
403
|
settleTreats(nowMs) {
|
|
383
404
|
const settlement = settleTreatGrants(this.persist.treats, this.persist.affinity.turns, nowMs, this.treatConfig);
|
|
384
|
-
if (settlement.
|
|
405
|
+
if (settlement.ledger !== this.persist.treats) {
|
|
385
406
|
this.persist = {
|
|
386
407
|
...this.persist,
|
|
387
408
|
treats: settlement.ledger
|
package/lib/invariant.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { l as defaultAffinityConfig, n as animationForPhase, o as AFFINITY_RANKS } from "./state-
|
|
1
|
+
import { l as defaultAffinityConfig, n as animationForPhase, o as AFFINITY_RANKS } from "./state-C9EyycwI.js";
|
|
2
2
|
//#region src/invariant.ts
|
|
3
3
|
/**
|
|
4
4
|
* Package invariants — cheap structural checks run at import time on the
|
|
@@ -159,7 +159,7 @@ var PetStateMachine = class {
|
|
|
159
159
|
this.config = config;
|
|
160
160
|
this.now = now;
|
|
161
161
|
}
|
|
162
|
-
/** Consume one
|
|
162
|
+
/** Consume one phase snapshot (fed by the service from session events). */
|
|
163
163
|
onActivityStatus(input) {
|
|
164
164
|
this.phase = input.phase;
|
|
165
165
|
this.line = input.line;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PluginSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PluginSettingsCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAGnD,wDAAwD;AACxD,MAAM,WAAW,uBAAuB;IACtC,0CAA0C;IAC1C,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,MAAM,CAAA;IACnC,uCAAuC;IACvC,QAAQ,EAAE,eAAe,CAAA;IACzB,4EAA4E;IAC5E,cAAc,EAAE,eAAe,CAAA;IAC/B,kFAAkF;IAClF,KAAK,EAAE,SAAS,CAAA;IAChB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,8BAA8B;IAC9B,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,6BAA6B;IAC7B,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,
|
|
1
|
+
{"version":3,"file":"PluginSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PluginSettingsCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAGnD,wDAAwD;AACxD,MAAM,WAAW,uBAAuB;IACtC,0CAA0C;IAC1C,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,MAAM,CAAA;IACnC,uCAAuC;IACvC,QAAQ,EAAE,eAAe,CAAA;IACzB,4EAA4E;IAC5E,cAAc,EAAE,eAAe,CAAA;IAC/B,kFAAkF;IAClF,KAAK,EAAE,SAAS,CAAA;IAChB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,8BAA8B;IAC9B,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,6BAA6B;IAC7B,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,sCA0GhE;AAED,oEAAoE;AACpE,MAAM,WAAW,UAAU;IACzB,wDAAwD;IACxD,EAAE,EAAE,MAAM,CAAA;IACV,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAA;IACZ,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAA;IACZ,sEAAsE;IACtE,UAAU,EAAE,OAAO,CAAA;IACnB,6DAA6D;IAC7D,OAAO,EAAE,OAAO,CAAA;IAChB,qCAAqC;IACrC,eAAe,EAAE,MAAM,CAAA;IACvB,kCAAkC;IAClC,UAAU,EAAE,MAAM,CAAA;IAClB,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAA;IACpB,gFAAgF;IAChF,QAAQ,EAAE,OAAO,CAAA;IACjB,wBAAwB;IACxB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,oEAAoE;IACpE,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAED,kHAAkH;AAClH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG;IAC7C,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,+BAqCA;AAED,0CAA0C;AAC1C,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG;IAC/C,mCAAmC;IACnC,YAAY,EAAE,MAAM,CAAA;IACpB,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAA;CACjB,+BAmCA"}
|
|
@@ -21,16 +21,17 @@ export function PluginSettingsCard(props) {
|
|
|
21
21
|
return null;
|
|
22
22
|
const title = props.t(props.titleKey);
|
|
23
23
|
const blocked = !state.dirty || state.invalid || state.saving;
|
|
24
|
+
const cardClass = open ? `${css.cardOpen} ${css.card}` : css.card;
|
|
24
25
|
// The namespace exists but the Host does not serve it to this client (the
|
|
25
26
|
// official settings allowlist omits third-party namespaces): show a card
|
|
26
27
|
// that explains the gap instead of vanishing, so a missing card never
|
|
27
28
|
// reads as a missing plugin.
|
|
28
29
|
if (!state.exposed) {
|
|
29
|
-
return (_jsxs("li", { className:
|
|
30
|
+
return (_jsxs("li", { className: cardClass, children: [_jsxs("button", { type: "button", className: css.header, "aria-expanded": open, "aria-label": `${props.t(open ? 'settings.collapse' : 'settings.expand')}: ${title}`, onClick: () => { setOpen(!open); }, children: [_jsxs("span", { className: css.headText, children: [_jsx("span", { className: css.name, children: title }), _jsx("span", { className: css.description, children: props.t(props.descriptionKey) })] }), _jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: open ? `${css.chevron} ${css.chevronOpen}` : css.chevron, children: _jsx("path", { d: "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z", fill: "currentColor" }) })] }), open
|
|
30
31
|
? (_jsx("div", { className: css.body, children: _jsx("p", { className: css.notExposed, role: "status", children: props.t('settings.notExposed') }) }))
|
|
31
32
|
: null] }));
|
|
32
33
|
}
|
|
33
|
-
return (_jsxs("li", { className:
|
|
34
|
+
return (_jsxs("li", { className: cardClass, children: [_jsxs("button", { type: "button", className: css.header, "aria-expanded": open, "aria-label": `${props.t(open ? 'settings.collapse' : 'settings.expand')}: ${title}`, onClick: () => { setOpen(!open); }, children: [_jsxs("span", { className: css.headText, children: [_jsx("span", { className: css.name, children: title }), _jsx("span", { className: css.description, children: props.t(props.descriptionKey) })] }), state.dirty ? _jsx("span", { className: css.pending, children: props.t('settings.unsaved') }) : null, _jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: open ? `${css.chevron} ${css.chevronOpen}` : css.chevron, children: _jsx("path", { d: "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z", fill: "currentColor" }) })] }), open
|
|
34
35
|
? (_jsxs("div", { className: css.body, children: [!state.writable ? _jsx("p", { className: css.readOnly, role: "status", children: props.t('settings.readOnly') }) : null, props.children, _jsxs("div", { className: css.footer, children: [state.failed ? _jsx("p", { className: css.failed, role: "status", children: props.t('settings.saveFailed') }) : null, _jsx("button", { type: "button", className: css.discard, disabled: !state.dirty || state.saving, onClick: props.onDiscard, children: props.t('settings.discard') }), _jsx("button", { type: "button", className: css.save, disabled: blocked, onClick: props.onSave, children: props.t(!state.saving ? 'settings.save' : 'settings.saving') })] })] }))
|
|
35
36
|
: null] }));
|
|
36
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WhalePet.d.ts","sourceRoot":"","sources":["../../../src/client/WhalePet.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAqC,WAAW,EAAE,MAAM,OAAO,CAAA;AAE3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAGjD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGjC,iFAAiF;AACjF,eAAO,MAAM,mBAAmB,gCAAgC,CAAA;AAEhE,mFAAmF;AACnF,eAAO,MAAM,gBAAgB,wBAAwB,CAAA;AAErD,wEAAwE;AACxE,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAA;IAC7B,qDAAqD;IACrD,OAAO,EAAE,gBAAgB,CAAA;IACzB,sCAAsC;IACtC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,kCAAkC;IAClC,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,0CAA0C;IAC1C,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,0CAA0C;IAC1C,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,+BAA+B;IAC/B,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD,8CAA8C;IAC9C,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,2DAA2D;IAC3D,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B,+CAA+C;IAC/C,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;CAC1B;AAOD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"WhalePet.d.ts","sourceRoot":"","sources":["../../../src/client/WhalePet.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAqC,WAAW,EAAE,MAAM,OAAO,CAAA;AAE3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAGjD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGjC,iFAAiF;AACjF,eAAO,MAAM,mBAAmB,gCAAgC,CAAA;AAEhE,mFAAmF;AACnF,eAAO,MAAM,gBAAgB,wBAAwB,CAAA;AAErD,wEAAwE;AACxE,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAA;IAC7B,qDAAqD;IACrD,OAAO,EAAE,gBAAgB,CAAA;IACzB,sCAAsC;IACtC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,kCAAkC;IAClC,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,0CAA0C;IAC1C,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,0CAA0C;IAC1C,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,+BAA+B;IAC/B,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD,8CAA8C;IAC9C,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,2DAA2D;IAC3D,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B,+CAA+C;IAC/C,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;CAC1B;AAOD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,WAAW,CAmT1D"}
|
|
@@ -234,6 +234,12 @@ export function WhalePet(props) {
|
|
|
234
234
|
// crossed the sliver between the sprite and the panel.
|
|
235
235
|
clearHideTimer();
|
|
236
236
|
}, children: renaming ? (_jsxs("div", { className: styles.renameRow, children: [_jsx("input", { className: styles.nameInput, value: nameDraft, maxLength: 20, placeholder: props.t('pet.namePlaceholder'), autoFocus: true, onChange: (e) => setNameDraft(e.target.value), onKeyDown: (e) => {
|
|
237
|
+
// While an IME composition is active (e.g. selecting a
|
|
238
|
+
// Chinese candidate), Enter/Escape keydowns belong to the
|
|
239
|
+
// input method: ignore them so candidate selection can
|
|
240
|
+
// neither submit the draft nor close the rename box.
|
|
241
|
+
if (e.nativeEvent.isComposing)
|
|
242
|
+
return;
|
|
237
243
|
if (e.key === 'Enter') {
|
|
238
244
|
const trimmed = nameDraft.trim();
|
|
239
245
|
if (trimmed !== '') {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* fixed-position summon button.
|
|
10
10
|
* @module @linxin666/dsh-pet/client
|
|
11
11
|
*/
|
|
12
|
-
import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
|
|
12
|
+
import type { ClientContext, SettingsScope, SettingsScopeSpec } from '@deepseek-ai/dsh-client-runtime/client';
|
|
13
13
|
/** Required services. */
|
|
14
14
|
export declare const inject: string[];
|
|
15
15
|
/** Re-exported for consumers that type against the injected face. */
|
|
@@ -36,6 +36,18 @@ export interface SettingsPluginItemOwnerProps {
|
|
|
36
36
|
/** Marker field: card owner props are intentionally empty. */
|
|
37
37
|
children?: never;
|
|
38
38
|
}
|
|
39
|
+
declare module '@deepseek-ai/cordis' {
|
|
40
|
+
interface Context {
|
|
41
|
+
/**
|
|
42
|
+
* Optional rc.6 compatibility binder provided by dsh-web-ui-settings;
|
|
43
|
+
* absent when that group plugin is not installed, so callers fall back to
|
|
44
|
+
* the official settings scope.
|
|
45
|
+
*/
|
|
46
|
+
webUiSettings?: {
|
|
47
|
+
bind<S>(spec: SettingsScopeSpec<S>): SettingsScope<S>;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
39
51
|
/**
|
|
40
52
|
* Client plugin body: register dictionaries, mount the global pet entry and
|
|
41
53
|
* poll loop while the plugin is enabled, and seat the settings card in the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAA;AAwD7G,yBAAyB;AACzB,eAAO,MAAM,MAAM,UAA+D,CAAA;AAElF,qEAAqE;AACrE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACxE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC7D,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AAEtF,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,OAAO;QACf;;;;;WAKG;QACH,oBAAoB,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,4BAA4B,CAAA;SAAE,CAAA;KAC3F;CACF;AAED,sEAAsE;AACtE,MAAM,WAAW,4BAA4B;IAC3C,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,KAAK,CAAA;CACjB;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf;;;;WAIG;QACH,aAAa,CAAC,EAAE;YAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;SAAE,CAAA;KAC1E;CACF;AAGD;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAoK9C"}
|
|
@@ -51,7 +51,8 @@ export const inject = ['slots', 'locale', 'connection', 'settingsScope', 'remote
|
|
|
51
51
|
*/
|
|
52
52
|
export function apply(ctx) {
|
|
53
53
|
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'pet: dictionaries');
|
|
54
|
-
const
|
|
54
|
+
const binder = ctx.get('webUiSettings') ?? ctx.settingsScope;
|
|
55
|
+
const settingsScope = binder.bind({ namespace: PET_SETTINGS_NS });
|
|
55
56
|
const enabled = () => {
|
|
56
57
|
const snapshot = settingsScope.getSnapshot();
|
|
57
58
|
return snapshot.status === 'ready'
|
package/lib/types/service.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pet host service — the `pet.*` RPC domain. Owns the state machine wiring
|
|
3
|
-
* (
|
|
4
|
-
*
|
|
5
|
-
* this service's methods onto
|
|
6
|
-
* `pet.
|
|
3
|
+
* (maps core rc.6 session events — turn/step/tool boundaries — and the
|
|
4
|
+
* session lifecycle onto the pet phases), the affinity ledger, and the
|
|
5
|
+
* persisted display config. The API gateway maps this service's methods onto
|
|
6
|
+
* `pet.state` / `pet.interact` / `pet.setVisible` / `pet.setConfig`
|
|
7
|
+
* for browser consumers.
|
|
7
8
|
* @module @linxin666/dsh-pet/service
|
|
8
9
|
*/
|
|
9
10
|
import { Context, Service } from '@deepseek-ai/cordis';
|
|
@@ -103,7 +104,8 @@ export declare class PetService extends Service {
|
|
|
103
104
|
private readonly treatConfig;
|
|
104
105
|
private readonly persistDir;
|
|
105
106
|
private persist;
|
|
106
|
-
|
|
107
|
+
/** Completed turns already rewarded, per session (turn numbers are per-session). */
|
|
108
|
+
private rewardedTurns;
|
|
107
109
|
private enabled;
|
|
108
110
|
private disposeActivity;
|
|
109
111
|
constructor(ctx: Context, config?: PetConfig);
|
|
@@ -147,11 +149,13 @@ export declare class PetService extends Service {
|
|
|
147
149
|
applySettingsSection(section: PetSettingsSection): void;
|
|
148
150
|
/** Mirror the persisted display config into the settings document (best-effort). */
|
|
149
151
|
private syncSettingsFromPet;
|
|
150
|
-
/** Award the turn reward once per
|
|
152
|
+
/** Award the turn reward once per completed turn (idempotent per session + turn). */
|
|
151
153
|
private rewardTurn;
|
|
152
154
|
/**
|
|
153
|
-
* Settle the treat economy (work + time output since the last
|
|
154
|
-
*
|
|
155
|
+
* Settle the treat economy (work + time output since the last settlement)
|
|
156
|
+
* and persist whenever the ledger changed. A zero-gain first settlement
|
|
157
|
+
* still starts the time clock (anchor write), which is what lets the
|
|
158
|
+
* 30-minute time output ever accrue.
|
|
155
159
|
*/
|
|
156
160
|
private settleTreats;
|
|
157
161
|
private view;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/service.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/service.ts"],"names":[],"mappings":"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"}
|
package/lib/types/service.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pet host service — the `pet.*` RPC domain. Owns the state machine wiring
|
|
3
|
-
* (
|
|
4
|
-
*
|
|
5
|
-
* this service's methods onto
|
|
6
|
-
* `pet.
|
|
3
|
+
* (maps core rc.6 session events — turn/step/tool boundaries — and the
|
|
4
|
+
* session lifecycle onto the pet phases), the affinity ledger, and the
|
|
5
|
+
* persisted display config. The API gateway maps this service's methods onto
|
|
6
|
+
* `pet.state` / `pet.interact` / `pet.setVisible` / `pet.setConfig`
|
|
7
|
+
* for browser consumers.
|
|
7
8
|
* @module @linxin666/dsh-pet/service
|
|
8
9
|
*/
|
|
9
10
|
import { Service } from '@deepseek-ai/cordis';
|
|
@@ -26,7 +27,8 @@ export class PetService extends Service {
|
|
|
26
27
|
treatConfig;
|
|
27
28
|
persistDir;
|
|
28
29
|
persist;
|
|
29
|
-
|
|
30
|
+
/** Completed turns already rewarded, per session (turn numbers are per-session). */
|
|
31
|
+
rewardedTurns = new Map();
|
|
30
32
|
enabled;
|
|
31
33
|
disposeActivity;
|
|
32
34
|
constructor(ctx, config = {}) {
|
|
@@ -72,24 +74,37 @@ export class PetService extends Service {
|
|
|
72
74
|
return;
|
|
73
75
|
this.disposeActivity = (() => {
|
|
74
76
|
const disposers = [
|
|
75
|
-
this.ctx.on('session/event', (
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
77
|
+
this.ctx.on('session/event', (session, event) => {
|
|
78
|
+
// rc.6 publishes no 'activity/status' event (the working-activity
|
|
79
|
+
// tracker is gone), so the pet derives its phases from the core
|
|
80
|
+
// session vocabulary instead.
|
|
81
|
+
switch (event.type) {
|
|
82
|
+
case 'turn/start':
|
|
83
|
+
this.machine.onSessionActive();
|
|
84
|
+
break;
|
|
85
|
+
case 'step/start':
|
|
86
|
+
this.machine.onSessionActive();
|
|
87
|
+
this.machine.onActivityStatus({ phase: 'thinking' });
|
|
88
|
+
break;
|
|
89
|
+
case 'tool/call':
|
|
90
|
+
this.machine.onSessionActive();
|
|
91
|
+
this.machine.onActivityStatus({ phase: 'tool', line: 'tool: ' + event.data.name });
|
|
92
|
+
break;
|
|
93
|
+
case 'turn/end':
|
|
94
|
+
this.machine.onSessionActive();
|
|
95
|
+
if (event.data.reason.kind === 'completed') {
|
|
96
|
+
this.machine.onActivityStatus({ phase: 'done' });
|
|
97
|
+
this.rewardTurn(String(session.id), event.data.turn);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// Aborted / failed turns clear the working pose instead of
|
|
101
|
+
// freezing the pet on its last phase.
|
|
102
|
+
this.machine.onActivityStatus({ phase: 'idle' });
|
|
103
|
+
}
|
|
104
|
+
break;
|
|
105
|
+
default:
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
93
108
|
}),
|
|
94
109
|
this.ctx.on('session/disposed', () => {
|
|
95
110
|
this.machine.onSessionDisposed();
|
|
@@ -191,23 +206,24 @@ export class PetService extends Service {
|
|
|
191
206
|
// A settings write failure must not break the pet's own persistence.
|
|
192
207
|
});
|
|
193
208
|
}
|
|
194
|
-
/** Award the turn reward once per
|
|
195
|
-
rewardTurn() {
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
if (nowMs - this.lastTurnRewardAt < 5_000)
|
|
209
|
+
/** Award the turn reward once per completed turn (idempotent per session + turn). */
|
|
210
|
+
rewardTurn(sessionId, turn) {
|
|
211
|
+
const last = this.rewardedTurns.get(sessionId) ?? 0;
|
|
212
|
+
if (turn <= last)
|
|
199
213
|
return;
|
|
200
|
-
this.
|
|
214
|
+
this.rewardedTurns.set(sessionId, turn);
|
|
201
215
|
this.persist = { ...this.persist, affinity: applyTurnReward(this.persist.affinity, this.affinityConfig) };
|
|
202
216
|
this.flush();
|
|
203
217
|
}
|
|
204
218
|
/**
|
|
205
|
-
* Settle the treat economy (work + time output since the last
|
|
206
|
-
*
|
|
219
|
+
* Settle the treat economy (work + time output since the last settlement)
|
|
220
|
+
* and persist whenever the ledger changed. A zero-gain first settlement
|
|
221
|
+
* still starts the time clock (anchor write), which is what lets the
|
|
222
|
+
* 30-minute time output ever accrue.
|
|
207
223
|
*/
|
|
208
224
|
settleTreats(nowMs) {
|
|
209
225
|
const settlement = settleTreatGrants(this.persist.treats, this.persist.affinity.turns, nowMs, this.treatConfig);
|
|
210
|
-
if (settlement.
|
|
226
|
+
if (settlement.ledger !== this.persist.treats) {
|
|
211
227
|
this.persist = { ...this.persist, treats: settlement.ledger };
|
|
212
228
|
this.flush();
|
|
213
229
|
}
|
package/lib/types/state.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pet state machine — pure, clock-injected. Maps the
|
|
3
|
-
*
|
|
4
|
-
* animation contract, plus the session lifecycle
|
|
5
|
-
* exposes (turn end celebration, no-session idle).
|
|
2
|
+
* Pet state machine — pure, clock-injected. Maps the pet's working-phase
|
|
3
|
+
* vocabulary (the service derives it from core session events) onto the
|
|
4
|
+
* 9-state Codex pet animation contract, plus the session lifecycle
|
|
5
|
+
* transitions the web UI exposes (turn end celebration, no-session idle).
|
|
6
6
|
*
|
|
7
7
|
* The machine is deliberately dumb: it holds the last input phase, the
|
|
8
8
|
* animation decision, and a one-shot "celebration" window after `done` so the
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
* function of (input, nowMs); persistence and RPC live in the service.
|
|
11
11
|
* @module @linxin666/dsh-pet/state
|
|
12
12
|
*/
|
|
13
|
-
/** The
|
|
13
|
+
/** The pet's working-phase vocabulary (derived from core session events by the service). */
|
|
14
14
|
export type ActivityPhase = 'idle' | 'waiting' | 'thinking' | 'tool' | 'done';
|
|
15
15
|
/** The Codex-compatible 9-state animation contract (spritesheet rows). */
|
|
16
16
|
export type PetAnimation = 'idle' | 'running-right' | 'running-left' | 'waving' | 'jumping' | 'failed' | 'waiting' | 'running' | 'review';
|
|
17
17
|
/** One input snapshot consumed by the machine. */
|
|
18
18
|
export interface PetStateInput {
|
|
19
|
-
/** Current
|
|
19
|
+
/** Current working phase of the active session. */
|
|
20
20
|
phase: ActivityPhase;
|
|
21
21
|
/** Human-readable status line (plain text). */
|
|
22
22
|
line?: string;
|
|
@@ -68,7 +68,7 @@ export declare class PetStateMachine {
|
|
|
68
68
|
private sessionActive;
|
|
69
69
|
private doneAt;
|
|
70
70
|
constructor(config?: PetStateConfig, now?: () => number);
|
|
71
|
-
/** Consume one
|
|
71
|
+
/** Consume one phase snapshot (fed by the service from session events). */
|
|
72
72
|
onActivityStatus(input: PetStateInput): void;
|
|
73
73
|
/** A session became the active one (or a fresh session started). */
|
|
74
74
|
onSessionActive(): void;
|
package/lib/types/state.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,4FAA4F;AAC5F,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,CAAA;AAE7E,0EAA0E;AAC1E,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,eAAe,GACf,cAAc,GACd,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,SAAS,GACT,SAAS,GACT,QAAQ,CAAA;AAEZ,kDAAkD;AAClD,MAAM,WAAW,aAAa;IAC5B,mDAAmD;IACnD,KAAK,EAAE,aAAa,CAAA;IACpB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,4DAA4D;AAC5D,MAAM,WAAW,gBAAgB;IAC/B,qCAAqC;IACrC,SAAS,EAAE,YAAY,CAAA;IACvB,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oEAAoE;IACpE,kBAAkB,EAAE,MAAM,CAAA;IAC1B,oEAAoE;IACpE,KAAK,EAAE,aAAa,CAAA;IACpB,0DAA0D;IAC1D,aAAa,EAAE,OAAO,CAAA;CACvB;AAED,6BAA6B;AAC7B,MAAM,WAAW,cAAc;IAC7B,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,eAAO,MAAM,qBAAqB,EAAE,cAAsC,CAAA;AAE1E;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,YAAY,CAQpE;AAED,yDAAyD;AACzD,wBAAgB,KAAK,CAAC,SAAS,EAAE,YAAY,GAAG,MAAM,CAarD;AAED;;;GAGG;AACH,qBAAa,eAAe;IAQxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IARtB,OAAO,CAAC,KAAK,CAAwB;IACrC,OAAO,CAAC,IAAI,CAAoB;IAChC,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,MAAM,CAAoB;gBAGf,MAAM,GAAE,cAAsC,EAC9C,GAAG,GAAE,MAAM,MAAiB;IAG/C,2EAA2E;IAC3E,gBAAgB,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAO5C,oEAAoE;IACpE,eAAe,IAAI,IAAI;IAIvB,sDAAsD;IACtD,iBAAiB,IAAI,IAAI;IAQzB,6CAA6C;IAC7C,MAAM,IAAI,gBAAgB;CAoB3B"}
|
package/lib/types/state.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pet state machine — pure, clock-injected. Maps the
|
|
3
|
-
*
|
|
4
|
-
* animation contract, plus the session lifecycle
|
|
5
|
-
* exposes (turn end celebration, no-session idle).
|
|
2
|
+
* Pet state machine — pure, clock-injected. Maps the pet's working-phase
|
|
3
|
+
* vocabulary (the service derives it from core session events) onto the
|
|
4
|
+
* 9-state Codex pet animation contract, plus the session lifecycle
|
|
5
|
+
* transitions the web UI exposes (turn end celebration, no-session idle).
|
|
6
6
|
*
|
|
7
7
|
* The machine is deliberately dumb: it holds the last input phase, the
|
|
8
8
|
* animation decision, and a one-shot "celebration" window after `done` so the
|
|
@@ -61,7 +61,7 @@ export class PetStateMachine {
|
|
|
61
61
|
this.config = config;
|
|
62
62
|
this.now = now;
|
|
63
63
|
}
|
|
64
|
-
/** Consume one
|
|
64
|
+
/** Consume one phase snapshot (fed by the service from session events). */
|
|
65
65
|
onActivityStatus(input) {
|
|
66
66
|
this.phase = input.phase;
|
|
67
67
|
this.line = input.line;
|
package/lib/types/treats.d.ts
CHANGED
|
@@ -42,8 +42,12 @@ export interface TreatSettlement {
|
|
|
42
42
|
* time output counts whole periods since the time anchor
|
|
43
43
|
* (`lastTreatGrantAt`) and advances only the time anchor. The two sources
|
|
44
44
|
* are independent so a continuously working user still earns time treats.
|
|
45
|
-
* 0 time history never backfills — the clock starts at the first settlement
|
|
46
|
-
*
|
|
45
|
+
* 0 time history never backfills — the clock starts at the first settlement,
|
|
46
|
+
* and even a zero-gain first settlement writes the time anchor so the next
|
|
47
|
+
* elapsed period can accrue (anchor deadlock fix). Both sources are clamped
|
|
48
|
+
* by the stock cap. When the anchor is already set and nothing is due, the
|
|
49
|
+
* input ledger is returned unchanged (same object), so callers can skip
|
|
50
|
+
* persistence cheaply.
|
|
47
51
|
*/
|
|
48
52
|
export declare function settleTreatGrants(ledger: TreatLedger, turns: number, nowMs: number, config?: TreatConfig): TreatSettlement;
|
|
49
53
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"treats.d.ts","sourceRoot":"","sources":["../../src/treats.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,4BAA4B;AAC5B,MAAM,WAAW,WAAW;IAC1B,6CAA6C;IAC7C,aAAa,EAAE,MAAM,CAAA;IACrB,2CAA2C;IAC3C,WAAW,EAAE,MAAM,CAAA;IACnB,kCAAkC;IAClC,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,eAAO,MAAM,kBAAkB,EAAE,WAIhC,CAAA;AAED,mDAAmD;AACnD,MAAM,WAAW,WAAW;IAC1B,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAA;IACd,iGAAiG;IACjG,gBAAgB,EAAE,MAAM,CAAA;IACxB,8EAA8E;IAC9E,qBAAqB,EAAE,MAAM,CAAA;CAC9B;AAED,wBAAgB,gBAAgB,IAAI,WAAW,CAE9C;AAED,sCAAsC;AACtC,MAAM,WAAW,eAAe;IAC9B,2CAA2C;IAC3C,MAAM,EAAE,WAAW,CAAA;IACnB,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAA;CACf;AAMD
|
|
1
|
+
{"version":3,"file":"treats.d.ts","sourceRoot":"","sources":["../../src/treats.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,4BAA4B;AAC5B,MAAM,WAAW,WAAW;IAC1B,6CAA6C;IAC7C,aAAa,EAAE,MAAM,CAAA;IACrB,2CAA2C;IAC3C,WAAW,EAAE,MAAM,CAAA;IACnB,kCAAkC;IAClC,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,eAAO,MAAM,kBAAkB,EAAE,WAIhC,CAAA;AAED,mDAAmD;AACnD,MAAM,WAAW,WAAW;IAC1B,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAA;IACd,iGAAiG;IACjG,gBAAgB,EAAE,MAAM,CAAA;IACxB,8EAA8E;IAC9E,qBAAqB,EAAE,MAAM,CAAA;CAC9B;AAED,wBAAgB,gBAAgB,IAAI,WAAW,CAE9C;AAED,sCAAsC;AACtC,MAAM,WAAW,eAAe;IAC9B,2CAA2C;IAC3C,MAAM,EAAE,WAAW,CAAA;IACnB,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAA;CACf;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,WAAgC,GACvC,eAAe,CA6BjB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,WAAW,GAClB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAGnD"}
|
package/lib/types/treats.js
CHANGED
|
@@ -26,8 +26,12 @@ function cap(treats, max) {
|
|
|
26
26
|
* time output counts whole periods since the time anchor
|
|
27
27
|
* (`lastTreatGrantAt`) and advances only the time anchor. The two sources
|
|
28
28
|
* are independent so a continuously working user still earns time treats.
|
|
29
|
-
* 0 time history never backfills — the clock starts at the first settlement
|
|
30
|
-
*
|
|
29
|
+
* 0 time history never backfills — the clock starts at the first settlement,
|
|
30
|
+
* and even a zero-gain first settlement writes the time anchor so the next
|
|
31
|
+
* elapsed period can accrue (anchor deadlock fix). Both sources are clamped
|
|
32
|
+
* by the stock cap. When the anchor is already set and nothing is due, the
|
|
33
|
+
* input ledger is returned unchanged (same object), so callers can skip
|
|
34
|
+
* persistence cheaply.
|
|
31
35
|
*/
|
|
32
36
|
export function settleTreatGrants(ledger, turns, nowMs, config = defaultTreatConfig) {
|
|
33
37
|
const turnDelta = Math.max(0, turns - ledger.turnsAtLastTreatGrant);
|
|
@@ -37,8 +41,15 @@ export function settleTreatGrants(ledger, turns, nowMs, config = defaultTreatCon
|
|
|
37
41
|
const timeAnchor = ledger.lastTreatGrantAt === 0 ? nowMs : ledger.lastTreatGrantAt;
|
|
38
42
|
const timeGrants = Math.floor(Math.max(0, nowMs - timeAnchor) / config.timeTreatMs);
|
|
39
43
|
const gained = workGrants + timeGrants;
|
|
40
|
-
if (gained <= 0)
|
|
44
|
+
if (gained <= 0) {
|
|
45
|
+
if (ledger.lastTreatGrantAt === 0) {
|
|
46
|
+
// Zero-gain first settlement: persist the clock start anyway, so the
|
|
47
|
+
// 30-minute time output can begin. Before this fix the anchor stayed 0
|
|
48
|
+
// forever (the deadlock: no grant means no anchor write means no grant).
|
|
49
|
+
return { ledger: { ...ledger, lastTreatGrantAt: nowMs }, gained: 0 };
|
|
50
|
+
}
|
|
41
51
|
return { ledger, gained: 0 };
|
|
52
|
+
}
|
|
42
53
|
return {
|
|
43
54
|
ledger: {
|
|
44
55
|
treats: cap(ledger.treats + gained, config.maxTreats),
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linxin666/dsh-pet",
|
|
3
3
|
"description": "鲸鱼娘宠物插件 for the dsh web GUI: a soft healing whale-girl companion that reacts to model activity (idle/waiting/thinking/tool/done), with petting/feeding interactions and an affinity score",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.13",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": "^22.19.0 || >=24.0.0"
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"cordis.patch.yml",
|
|
34
34
|
"assets"
|
|
35
35
|
],
|
|
36
|
-
"license": "
|
|
36
|
+
"license": "Apache-2.0",
|
|
37
37
|
"dsh": {
|
|
38
38
|
"bundle": {
|
|
39
39
|
"patch": "./cordis.patch.yml"
|
|
@@ -53,16 +53,6 @@
|
|
|
53
53
|
"schemastery": "^3.18.0"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@deepseek-ai/dsh-client-connection": "^0.1.0-rc.6",
|
|
57
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6",
|
|
58
|
-
"@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.6",
|
|
59
|
-
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6",
|
|
60
|
-
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.6",
|
|
61
|
-
"@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.6",
|
|
62
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6",
|
|
63
|
-
"@deepseek-ai/dsh-session": "^0.1.0-rc.6",
|
|
64
|
-
"@deepseek-ai/dsh-settings": "^0.1.0-rc.6",
|
|
65
|
-
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
|
|
66
56
|
"react": "^18.2.0",
|
|
67
57
|
"react-dom": "^18.2.0"
|
|
68
58
|
},
|